refactor(theme): improve theme structure
This commit is contained in:
parent
93adb30012
commit
862c886ff6
@ -12,14 +12,14 @@
|
|||||||
"vuepress": "2.0.0-rc.13"
|
"vuepress": "2.0.0-rc.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/json": "^2.2.217",
|
"@iconify/json": "^2.2.219",
|
||||||
"@vuepress/bundler-vite": "2.0.0-rc.13",
|
"@vuepress/bundler-vite": "2.0.0-rc.13",
|
||||||
"anywhere": "^1.6.0",
|
"anywhere": "^1.6.0",
|
||||||
"chart.js": "^4.4.3",
|
"chart.js": "^4.4.3",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"flowchart.ts": "^3.0.0",
|
"flowchart.ts": "^3.0.0",
|
||||||
"mermaid": "^10.9.1",
|
"mermaid": "^10.9.1",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.29",
|
||||||
"vuepress-theme-plume": "workspace:~"
|
"vuepress-theme-plume": "workspace:~"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "1.0.0-rc.64",
|
"version": "1.0.0-rc.64",
|
||||||
"private": true,
|
"private": true,
|
||||||
"packageManager": "pnpm@9.1.4",
|
"packageManager": "pnpm@9.3.0",
|
||||||
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
"author": "pengzhanbo <q942450674@outlook.com> (https://github.com/pengzhanbo/)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"eslint": "^9.4.0",
|
"eslint": "^9.4.0",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.0.11",
|
||||||
"lint-staged": "^15.2.5",
|
"lint-staged": "^15.2.7",
|
||||||
"rimraf": "^5.0.7",
|
"rimraf": "^5.0.7",
|
||||||
"stylelint": "^16.6.1",
|
"stylelint": "^16.6.1",
|
||||||
"tsconfig-vuepress": "^4.5.0",
|
"tsconfig-vuepress": "^4.5.0",
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"@vue/devtools-api": "6.6.1",
|
"@vue/devtools-api": "6.6.1",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"create-filter": "^1.0.1",
|
"create-filter": "^1.0.1",
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
"vuepress": "2.0.0-rc.13"
|
"vuepress": "2.0.0-rc.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vuepress-plume/plugin-content-update": "workspace:~",
|
"@vuepress-plume/plugin-content-update": "workspace:~",
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/vue": "^4.1.2",
|
"@iconify/vue": "^4.1.2",
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -48,17 +48,17 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/utils": "^2.1.24",
|
"@iconify/utils": "^2.1.24",
|
||||||
"@vuepress/helper": "2.0.0-rc.34",
|
"@vuepress/helper": "2.0.0-rc.34",
|
||||||
"@vueuse/core": "^10.10.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
"local-pkg": "^0.5.0",
|
"local-pkg": "^0.5.0",
|
||||||
"markdown-it-container": "^4.0.0",
|
"markdown-it-container": "^4.0.0",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"shiki": "^1.6.3",
|
"shiki": "^1.6.4",
|
||||||
"tm-grammars": "^1.12.8",
|
"tm-grammars": "^1.12.10",
|
||||||
"tm-themes": "^1.4.3",
|
"tm-themes": "^1.4.3",
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/json": "^2.2.217",
|
"@iconify/json": "^2.2.219",
|
||||||
"@types/markdown-it": "^14.1.1"
|
"@types/markdown-it": "^14.1.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|||||||
@ -50,9 +50,9 @@
|
|||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"cpx2": "^7.0.1",
|
"cpx2": "^7.0.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
"esbuild": "^0.21.4",
|
"esbuild": "^0.21.5",
|
||||||
"execa": "^9.2.0",
|
"execa": "^9.2.0",
|
||||||
"netlify-cli": "^17.26.0",
|
"netlify-cli": "^17.27.0",
|
||||||
"portfinder": "^1.0.32"
|
"portfinder": "^1.0.32"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"@vue/devtools-api": "6.6.1",
|
"@vue/devtools-api": "6.6.1",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"create-filter": "^1.0.1",
|
"create-filter": "^1.0.1",
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@netlify/functions": "^2.7.0",
|
"@netlify/functions": "^2.7.0",
|
||||||
"leancloud-storage": "^4.15.2",
|
"leancloud-storage": "^4.15.2",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.29",
|
||||||
"vue-router": "4.3.2",
|
"vue-router": "4.3.2",
|
||||||
"vuepress-plugin-netlify-functions": "workspace:~"
|
"vuepress-plugin-netlify-functions": "workspace:~"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -41,14 +41,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vuepress/helper": "2.0.0-rc.34",
|
"@vuepress/helper": "2.0.0-rc.34",
|
||||||
"@vueuse/core": "^10.10.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
"@vueuse/integrations": "^10.10.0",
|
"@vueuse/integrations": "^10.11.0",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"focus-trap": "^7.5.4",
|
"focus-trap": "^7.5.4",
|
||||||
"mark.js": "^8.11.1",
|
"mark.js": "^8.11.1",
|
||||||
"minisearch": "^6.3.0",
|
"minisearch": "^6.3.0",
|
||||||
"p-map": "^7.0.2",
|
"p-map": "^7.0.2",
|
||||||
"vue": "^3.4.27"
|
"vue": "^3.4.29"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
@ -36,18 +36,18 @@
|
|||||||
"vuepress": "2.0.0-rc.13"
|
"vuepress": "2.0.0-rc.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@shikijs/transformers": "^1.6.3",
|
"@shikijs/transformers": "^1.6.4",
|
||||||
"@shikijs/twoslash": "^1.6.3",
|
"@shikijs/twoslash": "^1.6.4",
|
||||||
"@types/hast": "^3.0.4",
|
"@types/hast": "^3.0.4",
|
||||||
"@vuepress/helper": "2.0.0-rc.34",
|
"@vuepress/helper": "2.0.0-rc.34",
|
||||||
"floating-vue": "^5.2.2",
|
"floating-vue": "^5.2.2",
|
||||||
"mdast-util-from-markdown": "^2.0.1",
|
"mdast-util-from-markdown": "^2.0.1",
|
||||||
"mdast-util-gfm": "^3.0.0",
|
"mdast-util-gfm": "^3.0.0",
|
||||||
"mdast-util-to-hast": "^13.1.0",
|
"mdast-util-to-hast": "^13.2.0",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"shiki": "^1.6.3",
|
"shiki": "^1.6.4",
|
||||||
"twoslash": "^0.2.6",
|
"twoslash": "^0.2.8",
|
||||||
"twoslash-vue": "^0.2.6"
|
"twoslash-vue": "^0.2.8"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
|||||||
1347
pnpm-lock.yaml
generated
1347
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -89,15 +89,15 @@
|
|||||||
"@vuepress/plugin-sitemap": "2.0.0-rc.34",
|
"@vuepress/plugin-sitemap": "2.0.0-rc.34",
|
||||||
"@vuepress/plugin-theme-data": "2.0.0-rc.34",
|
"@vuepress/plugin-theme-data": "2.0.0-rc.34",
|
||||||
"@vuepress/plugin-watermark": "2.0.0-rc.34",
|
"@vuepress/plugin-watermark": "2.0.0-rc.34",
|
||||||
"@vueuse/core": "^10.10.0",
|
"@vueuse/core": "^10.11.0",
|
||||||
"bcrypt-ts": "^5.0.2",
|
"bcrypt-ts": "^5.0.2",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"katex": "^0.16.10",
|
"katex": "^0.16.10",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"vue": "^3.4.27",
|
"vue": "^3.4.29",
|
||||||
"vue-router": "^4.3.2",
|
"vue-router": "^4.3.3",
|
||||||
"vuepress-plugin-md-enhance": "2.0.0-rc.48",
|
"vuepress-plugin-md-enhance": "2.0.0-rc.48",
|
||||||
"vuepress-plugin-md-power": "workspace:*"
|
"vuepress-plugin-md-power": "workspace:*"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { isLinkHttp } from 'vuepress/shared'
|
|||||||
import { useBlogExtract } from '../../composables/blog.js'
|
import { useBlogExtract } from '../../composables/blog.js'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import { inBrowser } from '../../utils/index.js'
|
import { inBrowser } from '../../utils/index.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@ -86,14 +86,14 @@ const showBlogExtract = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasBlogExtract" class="blog-nav" :class="{ 'no-avatar': !avatar }">
|
<div v-if="hasBlogExtract" class="blog-nav" :class="{ 'no-avatar': !avatar }">
|
||||||
<AutoLink class="nav-link" :href="tags.link" no-icon>
|
<VPLink class="nav-link" :href="tags.link" no-icon>
|
||||||
<span class="vpi-tag icon" />
|
<span class="vpi-tag icon" />
|
||||||
<span>{{ tags.text }}</span>
|
<span>{{ tags.text }}</span>
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
<AutoLink class="nav-link" :href="archives.link" no-icon>
|
<VPLink class="nav-link" :href="archives.link" no-icon>
|
||||||
<span class="vpi-archive icon" />
|
<span class="vpi-archive icon" />
|
||||||
<span>{{ archives.text }}</span>
|
<span>{{ archives.text }}</span>
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRoute } from 'vuepress/client'
|
import { useRoute } from 'vuepress/client'
|
||||||
import { useBlogExtract } from '../../composables/blog.js'
|
import { useBlogExtract } from '../../composables/blog.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
isLocal?: boolean
|
isLocal?: boolean
|
||||||
@ -14,7 +14,7 @@ const { hasBlogExtract, tags, archives } = useBlogExtract()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="hasBlogExtract" class="blog-nav" :class="{ local: props.isLocal }">
|
<div v-if="hasBlogExtract" class="blog-nav" :class="{ local: props.isLocal }">
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
:class="{ active: route.path === tags.link }"
|
:class="{ active: route.path === tags.link }"
|
||||||
:href="tags.link"
|
:href="tags.link"
|
||||||
@ -23,8 +23,8 @@ const { hasBlogExtract, tags, archives } = useBlogExtract()
|
|||||||
<span class="text">{{ tags.text }}</span>
|
<span class="text">{{ tags.text }}</span>
|
||||||
<span class="total">{{ tags.total }}</span>
|
<span class="total">{{ tags.total }}</span>
|
||||||
<span class="icon vpi-chevron-right" />
|
<span class="icon vpi-chevron-right" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="nav-link"
|
class="nav-link"
|
||||||
:class="{ active: route.path === archives.link }"
|
:class="{ active: route.path === archives.link }"
|
||||||
:href="archives.link"
|
:href="archives.link"
|
||||||
@ -33,7 +33,7 @@ const { hasBlogExtract, tags, archives } = useBlogExtract()
|
|||||||
<span class="text">{{ archives.text }}</span>
|
<span class="text">{{ archives.text }}</span>
|
||||||
<span class="total">{{ archives.total }}</span>
|
<span class="total">{{ archives.total }}</span>
|
||||||
<span class="icon vpi-chevron-right" />
|
<span class="icon vpi-chevron-right" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { computed } from 'vue'
|
|||||||
import { withBase } from 'vuepress/client'
|
import { withBase } from 'vuepress/client'
|
||||||
import { isLinkHttp } from 'vuepress/shared'
|
import { isLinkHttp } from 'vuepress/shared'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import SocialLinks from '../SocialLinks.vue'
|
import VPSocialLinks from '../VPSocialLinks.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
const avatar = computed(() => theme.value.avatar)
|
const avatar = computed(() => theme.value.avatar)
|
||||||
@ -35,7 +35,7 @@ const imageUrl = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="theme.social" class="avatar-social">
|
<div v-if="theme.social" class="avatar-social">
|
||||||
<SocialLinks :links="theme.social" />
|
<VPSocialLinks :links="theme.social" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -106,12 +106,12 @@ const imageUrl = computed(() => {
|
|||||||
transition: border var(--t-color);
|
transition: border var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-social :deep(.social-link) {
|
.avatar-social :deep(.vp-social-link) {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-social :deep(.social-link:hover) {
|
.avatar-social :deep(.vp-social-link:hover) {
|
||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { PlumeThemeBlogPostItem } from '../../../shared/index.js'
|
import type { PlumeThemeBlogPostItem } from '../../../shared/index.js'
|
||||||
import { useTagColors } from '../../composables/tag-colors.js'
|
import { useTagColors } from '../../composables/tag-colors.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
post: PlumeThemeBlogPostItem
|
post: PlumeThemeBlogPostItem
|
||||||
@ -38,9 +38,9 @@ const createTime = computed(() =>
|
|||||||
TOP
|
TOP
|
||||||
</div>
|
</div>
|
||||||
<span v-if="post.encrypt" class="icon-lock vpi-lock" />
|
<span v-if="post.encrypt" class="icon-lock vpi-lock" />
|
||||||
<AutoLink :href="post.path">
|
<VPLink :href="post.path">
|
||||||
{{ post.title }}
|
{{ post.title }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="post-meta">
|
<div class="post-meta">
|
||||||
<div v-if="categoryList.length" class="category-list">
|
<div v-if="categoryList.length" class="category-list">
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
postList: {
|
postList: {
|
||||||
@ -14,9 +14,9 @@ defineProps<{
|
|||||||
<ul class="post-list">
|
<ul class="post-list">
|
||||||
<li v-for="post in postList" :key="post.path">
|
<li v-for="post in postList" :key="post.path">
|
||||||
<p class="post-title">
|
<p class="post-title">
|
||||||
<AutoLink class="post-link" :href="post.path">
|
<VPLink class="post-link" :href="post.path">
|
||||||
{{ post.title }}
|
{{ post.title }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</p>
|
</p>
|
||||||
<span class="post-time">{{ post.createTime }}</span>
|
<span class="post-time">{{ post.createTime }}</span>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
import MenuLink from './MenuLink.vue'
|
import MenuLink from './MenuLink.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -12,7 +12,7 @@ defineProps<{
|
|||||||
<template>
|
<template>
|
||||||
<div class="menu-group">
|
<div class="menu-group">
|
||||||
<p v-if="text" class="title">
|
<p v-if="text" class="title">
|
||||||
<VIcon v-if="icon" :name="icon" />
|
<VPIcon v-if="icon" :name="icon" />
|
||||||
<span v-text="text" />
|
<span v-text="text" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import { isActive } from '../../utils/index.js'
|
import { isActive } from '../../utils/index.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
item: any
|
item: any
|
||||||
@ -13,7 +13,7 @@ const { page } = useData()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="menu-link">
|
<div class="menu-link">
|
||||||
<AutoLink
|
<VPLink
|
||||||
:class="{
|
:class="{
|
||||||
active: isActive(
|
active: isActive(
|
||||||
page.path,
|
page.path,
|
||||||
@ -23,9 +23,9 @@ const { page } = useData()
|
|||||||
}"
|
}"
|
||||||
:href="item.link"
|
:href="item.link"
|
||||||
>
|
>
|
||||||
<VIcon v-if="item.icon" :name="item.icon" />
|
<VPIcon v-if="item.icon" :name="item.icon" />
|
||||||
<i v-text="item.text" />
|
<i v-text="item.text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useFlyout } from '../../composables/flyout.js'
|
import { useFlyout } from '../../composables/flyout.js'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
import VMenu from './VMenu.vue'
|
import VMenu from './VMenu.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -45,7 +45,7 @@ export default {
|
|||||||
@click="open = !open"
|
@click="open = !open"
|
||||||
>
|
>
|
||||||
<span v-if="button || icon" class="text">
|
<span v-if="button || icon" class="text">
|
||||||
<VIcon v-if="prefixIcon" :name="prefixIcon" />
|
<VPIcon v-if="prefixIcon" :name="prefixIcon" />
|
||||||
<span v-if="icon" class="option-icon" :class="[icon]" />
|
<span v-if="icon" class="option-icon" :class="[icon]" />
|
||||||
<span v-if="button" v-html="button" />
|
<span v-if="button" v-html="button" />
|
||||||
<span class="vpi-chevron-down text-icon" />
|
<span class="vpi-chevron-down text-icon" />
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import { useEditNavLink } from '../composables/page.js'
|
import { useEditNavLink } from '../composables/page.js'
|
||||||
import AutoLink from './AutoLink.vue'
|
import VPLink from './VPLink.vue'
|
||||||
import FriendsItem from './FriendsItem.vue'
|
import FriendsItem from './FriendsItem.vue'
|
||||||
import FriendsGroup from './FriendsGroup.vue'
|
import FriendsGroup from './FriendsGroup.vue'
|
||||||
|
|
||||||
@ -32,14 +32,14 @@ const groups = computed(() => matter.value.groups || [])
|
|||||||
<FriendsGroup v-for="(group, index) in groups" :key="index" :group="group" />
|
<FriendsGroup v-for="(group, index) in groups" :key="index" :group="group" />
|
||||||
|
|
||||||
<div v-if="editNavLink" class="edit-link">
|
<div v-if="editNavLink" class="edit-link">
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="edit-link-button"
|
class="edit-link-button"
|
||||||
:href="editNavLink.link"
|
:href="editNavLink.link"
|
||||||
:no-icon="true"
|
:no-icon="true"
|
||||||
>
|
>
|
||||||
<span class="vpi-square-pen edit-link-icon" aria-label="edit icon" />
|
<span class="vpi-square-pen edit-link-icon" aria-label="edit icon" />
|
||||||
{{ editNavLink.text }}
|
{{ editNavLink.text }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { isPlainObject } from '@vuepress/helper/client'
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { FriendsItem } from '../../shared/index'
|
import type { FriendsItem } from '../../shared/index'
|
||||||
import { useDarkMode } from '../composables/dark-mode.js'
|
import { useDarkMode } from '../composables/dark-mode.js'
|
||||||
import AutoLink from './AutoLink.vue'
|
import VPLink from './VPLink.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
friend: FriendsItem
|
friend: FriendsItem
|
||||||
@ -30,7 +30,7 @@ const friendStyle = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="friend" :style="friendStyle">
|
<div class="friend" :style="friendStyle">
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="avatar-link"
|
class="avatar-link"
|
||||||
:href="friend.link"
|
:href="friend.link"
|
||||||
no-icon
|
no-icon
|
||||||
@ -39,16 +39,16 @@ const friendStyle = computed(() => {
|
|||||||
class="avatar"
|
class="avatar"
|
||||||
:style="{ backgroundImage: `url(${friend.avatar})` }"
|
:style="{ backgroundImage: `url(${friend.avatar})` }"
|
||||||
/>
|
/>
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="title"
|
class="title"
|
||||||
:href="friend.link"
|
:href="friend.link"
|
||||||
no-icon
|
no-icon
|
||||||
>
|
>
|
||||||
{{ friend.name }}
|
{{ friend.name }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
<p v-if="friend.desc">
|
<p v-if="friend.desc">
|
||||||
{{ friend.desc }}
|
{{ friend.desc }}
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@ -60,7 +60,7 @@ let el: HTMLDivElement | null = null
|
|||||||
|
|
||||||
watch(() => onlyOnce.value, value => nextTick(() => {
|
watch(() => onlyOnce.value, value => nextTick(() => {
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
el ??= document.querySelector('#LayoutContent')
|
el ??= document.querySelector('#VPContent')
|
||||||
el?.classList.toggle('footer-no-border', value)
|
el?.classList.toggle('footer-no-border', value)
|
||||||
}
|
}
|
||||||
}), { immediate: true })
|
}), { immediate: true })
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { isLinkHttp } from 'vuepress/shared'
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { PlumeThemeHomeBanner } from '../../../shared/index.js'
|
import type { PlumeThemeHomeBanner } from '../../../shared/index.js'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import VButton from '../VButton.vue'
|
import VPButton from '../VPButton.vue'
|
||||||
|
|
||||||
const props = defineProps<PlumeThemeHomeBanner>()
|
const props = defineProps<PlumeThemeHomeBanner>()
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ const actions = computed(() => props.hero?.actions ?? matter.value.hero?.actions
|
|||||||
|
|
||||||
<div v-if="actions.length" class="actions">
|
<div v-if="actions.length" class="actions">
|
||||||
<div v-for="action in actions" :key="action.link" class="action">
|
<div v-for="action in actions" :key="action.link" class="action">
|
||||||
<VButton
|
<VPButton
|
||||||
tag="a"
|
tag="a"
|
||||||
size="medium"
|
size="medium"
|
||||||
:theme="action.theme"
|
:theme="action.theme"
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PlumeThemeHomeFeature } from '../../../shared/index.js'
|
import type { PlumeThemeHomeFeature } from '../../../shared/index.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
import VImage from '../VImage.vue'
|
import VPImage from '../VPImage.vue'
|
||||||
|
|
||||||
defineProps<PlumeThemeHomeFeature>()
|
defineProps<PlumeThemeHomeFeature>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="home-feature"
|
class="home-feature"
|
||||||
:href="link"
|
:href="link"
|
||||||
:rel="rel"
|
:rel="rel"
|
||||||
@ -17,14 +17,14 @@ defineProps<PlumeThemeHomeFeature>()
|
|||||||
>
|
>
|
||||||
<article class="box">
|
<article class="box">
|
||||||
<div v-if="typeof icon === 'object' && icon.wrap" class="icon">
|
<div v-if="typeof icon === 'object' && icon.wrap" class="icon">
|
||||||
<VImage
|
<VPImage
|
||||||
:image="icon"
|
:image="icon"
|
||||||
:alt="icon.alt"
|
:alt="icon.alt"
|
||||||
:height="icon.height || 48"
|
:height="icon.height || 48"
|
||||||
:width="icon.width || 48"
|
:width="icon.width || 48"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<VImage
|
<VPImage
|
||||||
v-else-if="typeof icon === 'object'"
|
v-else-if="typeof icon === 'object'"
|
||||||
:image="icon"
|
:image="icon"
|
||||||
:alt="icon.alt"
|
:alt="icon.alt"
|
||||||
@ -41,7 +41,7 @@ defineProps<PlumeThemeHomeFeature>()
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { withBase } from 'vuepress/client'
|
import { withBase } from 'vuepress/client'
|
||||||
import { isLinkHttp } from 'vuepress/shared'
|
import { isLinkHttp } from 'vuepress/shared'
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import VButton from '../VButton.vue'
|
import VPButton from '../VPButton.vue'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import { useHomeHeroTintPlate } from '../../composables/home.js'
|
import { useHomeHeroTintPlate } from '../../composables/home.js'
|
||||||
import type { PlumeThemeHomeHero } from '../../../shared/index.js'
|
import type { PlumeThemeHomeHero } from '../../../shared/index.js'
|
||||||
@ -59,9 +59,14 @@ useHomeHeroTintPlate(
|
|||||||
|
|
||||||
<div v-if="actions.length" class="actions">
|
<div v-if="actions.length" class="actions">
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<VButton
|
<VPButton
|
||||||
v-for="action in actions" :key="action.link" tag="a" size="medium" :theme="action.theme"
|
v-for="action in actions"
|
||||||
:text="action.text" :href="action.link"
|
:key="action.link"
|
||||||
|
tag="a"
|
||||||
|
size="medium"
|
||||||
|
:theme="action.theme"
|
||||||
|
:text="action.text"
|
||||||
|
:href="action.link"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -162,11 +167,11 @@ useHomeHeroTintPlate(
|
|||||||
margin: 30px 0 0;
|
margin: 30px 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action :deep(.VPButton) {
|
.action :deep(.vp-button) {
|
||||||
margin-right: 24px;
|
margin-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action :deep(.VPButton:last-of-type) {
|
.action :deep(.vp-button:last-of-type) {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { PlumeThemeHomeProfile } from '../../../shared/index.js'
|
import type { PlumeThemeHomeProfile } from '../../../shared/index.js'
|
||||||
import VImage from '../VImage.vue'
|
import VPImage from '../VPImage.vue'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import HomeBox from './HomeBox.vue'
|
import HomeBox from './HomeBox.vue'
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ const profile = computed(() => {
|
|||||||
:background-attachment="backgroundAttachment"
|
:background-attachment="backgroundAttachment"
|
||||||
:full="full"
|
:full="full"
|
||||||
>
|
>
|
||||||
<VImage v-if="profile.avatar" :image="profile.avatar" :class="{ circle: profile.circle }" />
|
<VPImage v-if="profile.avatar" :image="profile.avatar" :class="{ circle: profile.circle }" />
|
||||||
|
|
||||||
<h3 v-if="profile.name" v-html="profile.name" />
|
<h3 v-if="profile.name" v-html="profile.name" />
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { PlumeThemeHomeTextImage } from '../../../shared/index.js'
|
import type { PlumeThemeHomeTextImage } from '../../../shared/index.js'
|
||||||
import VImage from '../VImage.vue'
|
import VPImage from '../VPImage.vue'
|
||||||
import HomeBox from './HomeBox.vue'
|
import HomeBox from './HomeBox.vue'
|
||||||
|
|
||||||
const props = defineProps<PlumeThemeHomeTextImage>()
|
const props = defineProps<PlumeThemeHomeTextImage>()
|
||||||
@ -26,7 +26,7 @@ const maxWidth = computed(() => {
|
|||||||
:container-class="{ reverse: type === 'text-image' }"
|
:container-class="{ reverse: type === 'text-image' }"
|
||||||
>
|
>
|
||||||
<div class="content-image">
|
<div class="content-image">
|
||||||
<VImage :image="image" :style="{ maxWidth }" />
|
<VPImage :image="image" :style="{ maxWidth }" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content-text plume-content">
|
<div class="content-text plume-content">
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { computed } from 'vue'
|
|
||||||
import { useData, useSidebar } from '../composables/index.js'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
isNotFound?: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const { hasSidebar } = useSidebar()
|
|
||||||
const { theme, frontmatter } = useData()
|
|
||||||
|
|
||||||
const enabledExternalIcon = computed(() => {
|
|
||||||
return frontmatter.value.externalLink ?? theme.value.externalLinkIcon ?? true
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
id="LayoutContent" class="layout-content" :class="{
|
|
||||||
'has-sidebar': hasSidebar && !props.isNotFound,
|
|
||||||
'external-link-icon-enabled': enabledExternalIcon,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.layout-content {
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 100%;
|
|
||||||
padding-left: 0;
|
|
||||||
margin: var(--vp-layout-top-height, 0) auto 0;
|
|
||||||
transition: padding-left 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-content.is-home {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-content.has-sidebar {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
|
||||||
.layout-content {
|
|
||||||
padding-top: var(--vp-nav-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
.layout-content.has-sidebar {
|
|
||||||
padding-left: var(--vp-sidebar-width);
|
|
||||||
margin: var(--vp-layout-top-height, 0) 0 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1440px) {
|
|
||||||
.layout-content.has-sidebar {
|
|
||||||
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2);
|
|
||||||
padding-left:
|
|
||||||
calc(
|
|
||||||
(100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import type { MenuItem } from '../../composables/outline.js'
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
headers: MenuItem[]
|
|
||||||
root?: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
function onClick({ target: el }: Event) {
|
|
||||||
const id = (el as HTMLAnchorElement).href!.split('#')[1]
|
|
||||||
const heading = document.getElementById(decodeURIComponent(id))
|
|
||||||
heading?.focus({ preventScroll: true })
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<ul :class="root ? 'root' : 'nested'">
|
|
||||||
<li v-for="{ children, link, title } in headers" :key="link">
|
|
||||||
<a class="outline-link" :href="link" :title="title" @click="onClick">{{ title }}</a>
|
|
||||||
<template v-if="children?.length">
|
|
||||||
<DocOutlineItem :headers="children" />
|
|
||||||
</template>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.root {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nested {
|
|
||||||
padding-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline-link {
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 28px;
|
|
||||||
color: var(--vp-c-text-2);
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
transition: color var(--t-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline-link:hover,
|
|
||||||
.outline-link.active {
|
|
||||||
color: var(--vp-c-text-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.outline-link.nested {
|
|
||||||
padding-left: 13px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -26,7 +26,7 @@ const classes = ref<Record<string, boolean>>({})
|
|||||||
watchPostEffect(() => {
|
watchPostEffect(() => {
|
||||||
classes.value = {
|
classes.value = {
|
||||||
'has-sidebar': hasSidebar.value,
|
'has-sidebar': hasSidebar.value,
|
||||||
'top': !!frontmatter.value.home && y.value === 0,
|
'top': frontmatter.value.pageLayout === 'home' && y.value === 0,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import SwitchAppearance from '../SwitchAppearance.vue'
|
import VPSwitchAppearance from '../VPSwitchAppearance.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
</script>
|
</script>
|
||||||
@ -10,7 +10,7 @@ const { theme } = useData()
|
|||||||
v-if="theme.appearance && theme.appearance !== 'force-dark'"
|
v-if="theme.appearance && theme.appearance !== 'force-dark'"
|
||||||
class="navbar-appearance"
|
class="navbar-appearance"
|
||||||
>
|
>
|
||||||
<SwitchAppearance />
|
<VPSwitchAppearance />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { useData } from '../../composables/data.js'
|
|||||||
import { useLangs } from '../../composables/langs.js'
|
import { useLangs } from '../../composables/langs.js'
|
||||||
import Flyout from '../Flyout/index.vue'
|
import Flyout from '../Flyout/index.vue'
|
||||||
import MenuLink from '../Flyout/MenuLink.vue'
|
import MenuLink from '../Flyout/MenuLink.vue'
|
||||||
import SocialLinks from '../SocialLinks.vue'
|
import VPSocialLinks from '../VPSocialLinks.vue'
|
||||||
import SwitchAppearance from '../SwitchAppearance.vue'
|
import VPSwitchAppearance from '../VPSwitchAppearance.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
const { localeLinks, currentLang } = useLangs()
|
const { localeLinks, currentLang } = useLangs()
|
||||||
@ -46,14 +46,14 @@ const social = computed(() => {
|
|||||||
{{ theme.appearanceText || 'Appearance' }}
|
{{ theme.appearanceText || 'Appearance' }}
|
||||||
</p>
|
</p>
|
||||||
<div class="appearance-action">
|
<div class="appearance-action">
|
||||||
<SwitchAppearance />
|
<VPSwitchAppearance />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="social" class="group">
|
<div v-if="social" class="group">
|
||||||
<div class="item social-links">
|
<div class="item social-links">
|
||||||
<SocialLinks class="social-links-list" :links="social" />
|
<VPSocialLinks class="social-links-list" :links="social" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Flyout>
|
</Flyout>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { NavItemWithLink } from '../../../shared/index.js'
|
import type { NavItemWithLink } from '../../../shared/index.js'
|
||||||
import { isActive } from '../../utils/index.js'
|
import { isActive } from '../../utils/index.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -13,7 +13,7 @@ const { page } = useData()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="navbar-menu-link" :class="{
|
class="navbar-menu-link" :class="{
|
||||||
active: isActive(
|
active: isActive(
|
||||||
page.path,
|
page.path,
|
||||||
@ -24,9 +24,9 @@ const { page } = useData()
|
|||||||
:href="item.link"
|
:href="item.link"
|
||||||
:no-icon="true"
|
:no-icon="true"
|
||||||
>
|
>
|
||||||
<VIcon v-if="item.icon" :name="item.icon" />
|
<VPIcon v-if="item.icon" :name="item.icon" />
|
||||||
<i v-text="item.text" />
|
<i v-text="item.text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import SocialLinks from '../SocialLinks.vue'
|
import VPSocialLinks from '../VPSocialLinks.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ const social = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SocialLinks
|
<VPSocialLinks
|
||||||
v-if="social"
|
v-if="social"
|
||||||
class="navbar-social-links"
|
class="navbar-social-links"
|
||||||
:links="social"
|
:links="social"
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
import { useRouteLocale } from 'vuepress/client'
|
import { useRouteLocale } from 'vuepress/client'
|
||||||
import { useSidebar } from '../../composables/sidebar.js'
|
import { useSidebar } from '../../composables/sidebar.js'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
import VImage from '../VImage.vue'
|
import VPImage from '../VPImage.vue'
|
||||||
|
|
||||||
const { theme, site } = useData()
|
const { theme, site } = useData()
|
||||||
const { hasSidebar } = useSidebar()
|
const { hasSidebar } = useSidebar()
|
||||||
@ -12,14 +12,14 @@ const routeLocale = useRouteLocale()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="navbar-title" :class="{ 'has-sidebar': hasSidebar }">
|
<div class="navbar-title" :class="{ 'has-sidebar': hasSidebar }">
|
||||||
<AutoLink class="title" :href="theme.home ?? routeLocale">
|
<VPLink class="title" :href="theme.home ?? routeLocale">
|
||||||
<VImage
|
<VPImage
|
||||||
v-if="theme.logo"
|
v-if="theme.logo"
|
||||||
class="logo"
|
class="logo"
|
||||||
:image="{ light: theme.logo, dark: theme.logoDark || theme.logo }"
|
:image="{ light: theme.logo, dark: theme.logoDark || theme.logo }"
|
||||||
/>
|
/>
|
||||||
{{ site.title }}
|
{{ site.title }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import SwitchAppearance from '../SwitchAppearance.vue'
|
import VPSwitchAppearance from '../VPSwitchAppearance.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
</script>
|
</script>
|
||||||
@ -13,7 +13,7 @@ const { theme } = useData()
|
|||||||
<p class="text">
|
<p class="text">
|
||||||
{{ theme.appearanceText ?? 'Appearance' }}
|
{{ theme.appearanceText ?? 'Appearance' }}
|
||||||
</p>
|
</p>
|
||||||
<SwitchAppearance />
|
<VPSwitchAppearance />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
import NavScreenMenuGroupLink from './NavScreenMenuGroupLink.vue'
|
import NavScreenMenuGroupLink from './NavScreenMenuGroupLink.vue'
|
||||||
import NavScreenMenuGroupSection from './NavScreenMenuGroupSection.vue'
|
import NavScreenMenuGroupSection from './NavScreenMenuGroupSection.vue'
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ function toggle() {
|
|||||||
@click="toggle"
|
@click="toggle"
|
||||||
>
|
>
|
||||||
<span class="button-text">
|
<span class="button-text">
|
||||||
<VIcon v-if="icon" :name="icon" />
|
<VPIcon v-if="icon" :name="icon" />
|
||||||
<i v-text="text" />
|
<i v-text="text" />
|
||||||
</span>
|
</span>
|
||||||
<span class="vpi-plus button-icon" />
|
<span class="vpi-plus button-icon" />
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
icon?: string | { svg: string }
|
icon?: string | { svg: string }
|
||||||
@ -13,14 +13,14 @@ const closeScreen = inject('close-screen') as () => void
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AutoLink
|
<VPLink
|
||||||
class="nav-screen-menu-group-link"
|
class="nav-screen-menu-group-link"
|
||||||
:href="link"
|
:href="link"
|
||||||
@click="closeScreen"
|
@click="closeScreen"
|
||||||
>
|
>
|
||||||
<VIcon v-if="icon" :name="icon" />
|
<VPIcon v-if="icon" :name="icon" />
|
||||||
<i v-text="text" />
|
<i v-text="text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { NavItemWithLink } from '../../../shared/index.js'
|
import type { NavItemWithLink } from '../../../shared/index.js'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
import NavScreenMenuGroupLink from './NavScreenMenuGroupLink.vue'
|
import NavScreenMenuGroupLink from './NavScreenMenuGroupLink.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -13,7 +13,7 @@ defineProps<{
|
|||||||
<template>
|
<template>
|
||||||
<div class="nav-screen-menu-group-section">
|
<div class="nav-screen-menu-group-section">
|
||||||
<p v-if="text" class="title">
|
<p v-if="text" class="title">
|
||||||
<VIcon v-if="icon" :name="icon" />
|
<VPIcon v-if="icon" :name="icon" />
|
||||||
{{ text }}
|
{{ text }}
|
||||||
</p>
|
</p>
|
||||||
<NavScreenMenuGroupLink
|
<NavScreenMenuGroupLink
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { inject } from 'vue'
|
import { inject } from 'vue'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
import VIcon from '../VIcon.vue'
|
import VPIcon from '../VPIcon.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
text: string
|
text: string
|
||||||
@ -13,10 +13,10 @@ const closeScreen = inject('close-screen') as () => void
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<AutoLink class="nav-screen-menu-link" :href="link" @click="closeScreen">
|
<VPLink class="nav-screen-menu-link" :href="link" @click="closeScreen">
|
||||||
<VIcon v-if="icon" :name="icon" />
|
<VPIcon v-if="icon" :name="icon" />
|
||||||
<i v-text="text" />
|
<i v-text="text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../../composables/data.js'
|
||||||
import SocialLinks from '../SocialLinks.vue'
|
import VPSocialLinks from '../VPSocialLinks.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SocialLinks
|
<VPSocialLinks
|
||||||
v-if="theme.social"
|
v-if="theme.social"
|
||||||
class="VPNavScreenSocialLinks"
|
class="VPNavScreenSocialLinks"
|
||||||
:links="theme.social"
|
:links="theme.social"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useLangs } from '../../composables/langs.js'
|
import { useLangs } from '../../composables/langs.js'
|
||||||
import AutoLink from '../AutoLink.vue'
|
import VPLink from '../VPLink.vue'
|
||||||
|
|
||||||
const { localeLinks, currentLang } = useLangs()
|
const { localeLinks, currentLang } = useLangs()
|
||||||
const isOpen = ref(false)
|
const isOpen = ref(false)
|
||||||
@ -25,9 +25,9 @@ function toggle() {
|
|||||||
|
|
||||||
<ul class="list">
|
<ul class="list">
|
||||||
<li v-for="locale in localeLinks" :key="locale.link" class="item">
|
<li v-for="locale in localeLinks" :key="locale.link" class="item">
|
||||||
<AutoLink class="link" :href="locale.link">
|
<VPLink class="link" :href="locale.link">
|
||||||
{{ locale.text }}
|
{{ locale.text }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,7 +12,7 @@ const { isScreenOpen, closeScreen, toggleScreen } = useNav()
|
|||||||
const fixedInclude = ['blog', 'friends', 'blog-archives', 'blog-tags']
|
const fixedInclude = ['blog', 'friends', 'blog-archives', 'blog-tags']
|
||||||
|
|
||||||
const fixed = computed(() => {
|
const fixed = computed(() => {
|
||||||
return fixedInclude.includes(page.value.frontmatter.type as string)
|
return fixedInclude.includes(page.value.type as string)
|
||||||
})
|
})
|
||||||
|
|
||||||
provide('close-screen', closeScreen)
|
provide('close-screen', closeScreen)
|
||||||
|
|||||||
@ -1,237 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import { computed, nextTick, ref, watch } from 'vue'
|
|
||||||
import { useRoute } from 'vuepress/client'
|
|
||||||
import { useData } from '../composables/data.js'
|
|
||||||
import { useSidebar } from '../composables/sidebar.js'
|
|
||||||
import { usePageEncrypt } from '../composables/encrypt.js'
|
|
||||||
import PageAside from './PageAside.vue'
|
|
||||||
import PageFooter from './PageFooter.vue'
|
|
||||||
import PageMeta from './PageMeta.vue'
|
|
||||||
import EncryptPage from './EncryptPage.vue'
|
|
||||||
import TransitionFadeSlideY from './TransitionFadeSlideY.vue'
|
|
||||||
|
|
||||||
const { hasSidebar, hasAside } = useSidebar()
|
|
||||||
const { page, isDark } = useData()
|
|
||||||
const route = useRoute()
|
|
||||||
|
|
||||||
const { isPageDecrypted } = usePageEncrypt()
|
|
||||||
|
|
||||||
const hasComments = computed(() => {
|
|
||||||
return page.value.frontmatter.comments !== false
|
|
||||||
})
|
|
||||||
|
|
||||||
const enableAside = computed(() => {
|
|
||||||
if (page.value.isBlogPost)
|
|
||||||
return hasAside.value && isPageDecrypted.value && page.value.headers.length
|
|
||||||
|
|
||||||
return hasAside.value && isPageDecrypted.value
|
|
||||||
})
|
|
||||||
|
|
||||||
const asideEl = ref<HTMLElement>()
|
|
||||||
watch(
|
|
||||||
() => route.hash,
|
|
||||||
hash =>
|
|
||||||
nextTick(() => {
|
|
||||||
if (!asideEl.value)
|
|
||||||
return
|
|
||||||
const activeItem = asideEl.value.querySelector(
|
|
||||||
`.outline-link[href="${hash}"]`,
|
|
||||||
)
|
|
||||||
if (!activeItem || !hash) {
|
|
||||||
asideEl.value.scrollTop = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { top: navTop, height: navHeight }
|
|
||||||
= asideEl.value.getBoundingClientRect()
|
|
||||||
const { top: activeTop, height: activeHeight }
|
|
||||||
= activeItem.getBoundingClientRect()
|
|
||||||
|
|
||||||
if (activeTop < navTop || activeTop + activeHeight > navTop + navHeight)
|
|
||||||
activeItem.scrollIntoView({ block: 'center' })
|
|
||||||
}),
|
|
||||||
{ immediate: true },
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<TransitionFadeSlideY>
|
|
||||||
<div
|
|
||||||
:key="page.path" class="plume-page" :class="{
|
|
||||||
'has-sidebar': hasSidebar,
|
|
||||||
'has-aside': hasAside,
|
|
||||||
'is-blog': page.isBlogPost,
|
|
||||||
'with-encrypt': !isPageDecrypted,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="container">
|
|
||||||
<div v-if="enableAside" class="aside">
|
|
||||||
<div ref="asideEl" class="aside-container">
|
|
||||||
<div class="aside-content">
|
|
||||||
<PageAside />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="content-container">
|
|
||||||
<PageMeta />
|
|
||||||
<EncryptPage v-if="!isPageDecrypted" />
|
|
||||||
<template v-else>
|
|
||||||
<Content class="plume-content" />
|
|
||||||
<PageFooter />
|
|
||||||
<PageComment v-if="hasComments" :darkmode="isDark" />
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TransitionFadeSlideY>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.plume-page {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page {
|
|
||||||
width: 100%;
|
|
||||||
min-height: calc(100vh - var(--vp-nav-height) - var(--vp-footer-height, 0px) - 49px);
|
|
||||||
padding: 32px 24px 96px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page.with-encrypt {
|
|
||||||
padding: 32px 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside {
|
|
||||||
position: relative;
|
|
||||||
display: none;
|
|
||||||
flex-grow: 1;
|
|
||||||
order: 2;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 256px;
|
|
||||||
padding-left: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-container {
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
min-height: calc(100vh - var(--vp-footer-height, 0px));
|
|
||||||
max-height: 100vh;
|
|
||||||
padding-top:
|
|
||||||
calc(
|
|
||||||
var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px
|
|
||||||
);
|
|
||||||
margin-top:
|
|
||||||
calc(
|
|
||||||
(var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1 - 32px
|
|
||||||
);
|
|
||||||
overflow: hidden auto;
|
|
||||||
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-container::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height:
|
|
||||||
calc(
|
|
||||||
100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px + var(--vp-footer-height, 0px))
|
|
||||||
);
|
|
||||||
padding-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page.has-aside .content-container {
|
|
||||||
max-width: 828px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.giscus-wrapper {
|
|
||||||
padding: 5rem 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.plume-page {
|
|
||||||
padding: 48px 32px 128px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
|
||||||
.plume-page {
|
|
||||||
min-height: calc(100vh - var(--vp-nav-height) - var(--vp-footer-height, 0px));
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page,
|
|
||||||
.plume-page.is-blog {
|
|
||||||
padding: 32px 32px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page .container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page.is-blog .aside {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page:not(.has-sidebar) .container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
max-width: 992px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page:not(.has-sidebar) .content {
|
|
||||||
max-width: 752px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
|
||||||
.plume-page .aside {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1440px) {
|
|
||||||
.plume-page:not(.has-sidebar) .content {
|
|
||||||
max-width: 884px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.plume-page:not(.has-sidebar) .container {
|
|
||||||
max-width: 1404px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
|
||||||
.content {
|
|
||||||
padding: 0 32px 128px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
|
||||||
.content {
|
|
||||||
order: 1;
|
|
||||||
min-width: 640px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -27,7 +27,7 @@ const stroke = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const mustHidden = computed(() => {
|
const mustHidden = computed(() => {
|
||||||
return page.value.frontmatter.backToTop === false || (page.value.frontmatter.home && page.value.frontmatter.config && (page.value.frontmatter.config as any).length <= 1)
|
return page.value.frontmatter.backToTop === false || (page.value.frontmatter.pageLayout === 'home' && page.value.frontmatter.config && (page.value.frontmatter.config as any).length <= 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
const show = computed(() => {
|
const show = computed(() => {
|
||||||
@ -60,7 +60,7 @@ function handleClick() {
|
|||||||
<button
|
<button
|
||||||
v-show="!mustHidden && (show || isScrolling)"
|
v-show="!mustHidden && (show || isScrolling)"
|
||||||
type="button"
|
type="button"
|
||||||
class="back-to-top-button"
|
class="vp-back-to-top"
|
||||||
aria-label="back to top"
|
aria-label="back to top"
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
@ -74,7 +74,7 @@ function handleClick() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.back-to-top-button {
|
.vp-back-to-top {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
inset-inline-end: 1rem;
|
inset-inline-end: 1rem;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
@ -90,8 +90,8 @@ function handleClick() {
|
|||||||
box-shadow var(--t-color);
|
box-shadow var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button .percent,
|
.vp-back-to-top .percent,
|
||||||
.back-to-top-button .icon {
|
.vp-back-to-top .icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -99,12 +99,12 @@ function handleClick() {
|
|||||||
transition: opacity 0.5s ease, color var(--t-color);
|
transition: opacity 0.5s ease, color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button .percent.show,
|
.vp-back-to-top .percent.show,
|
||||||
.back-to-top-button .icon.show {
|
.vp-back-to-top .icon.show {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button .percent {
|
.vp-back-to-top .percent {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@ -113,7 +113,7 @@ function handleClick() {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button .icon {
|
.vp-back-to-top .icon {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
@ -122,12 +122,12 @@ function handleClick() {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button svg {
|
.vp-back-to-top svg {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button svg circle {
|
.vp-back-to-top svg circle {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: var(--vp-c-brand-2);
|
stroke: var(--vp-c-brand-2);
|
||||||
stroke-dasharray: 0% 314.1593%;
|
stroke-dasharray: 0% 314.1593%;
|
||||||
@ -139,23 +139,23 @@ function handleClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.back-to-top-button {
|
.vp-back-to-top {
|
||||||
bottom: calc(var(--vp-footer-height, 88px) - 24px);
|
bottom: calc(var(--vp-footer-height, 88px) - 24px);
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button .percent {
|
.vp-back-to-top .percent {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 48px;
|
line-height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button .icon {
|
.vp-back-to-top .icon {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top-button svg circle {
|
.vp-back-to-top svg circle {
|
||||||
r: 22;
|
r: 22;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -171,7 +171,7 @@ function handleClick() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
.back-to-top-button {
|
.vp-back-to-top {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,12 +6,12 @@ defineProps<{
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Transition name="fade">
|
<Transition name="fade">
|
||||||
<div v-if="show" class="backdrop" />
|
<div v-if="show" class="vp-backdrop" />
|
||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.backdrop {
|
.vp-backdrop {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
@ -26,17 +26,17 @@ defineProps<{
|
|||||||
transition: opacity var(--t-color);
|
transition: opacity var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.backdrop.fade-enter-from,
|
.vp-backdrop.fade-enter-from,
|
||||||
.backdrop.fade-leave-to {
|
.vp-backdrop.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.backdrop.fade-leave-active {
|
.vp-backdrop.fade-leave-active {
|
||||||
transition-duration: 0.25s;
|
transition-duration: 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
@media (min-width: 1280px) {
|
||||||
.backdrop {
|
.vp-backdrop {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,8 +38,7 @@ function linkTo(e: Event) {
|
|||||||
<template>
|
<template>
|
||||||
<Component
|
<Component
|
||||||
:is="component"
|
:is="component"
|
||||||
class="VPButton"
|
class="vp-button" :class="[size, theme]"
|
||||||
:class="[size, theme]"
|
|
||||||
:href="href"
|
:href="href"
|
||||||
:target="isExternal ? '_blank' : undefined"
|
:target="isExternal ? '_blank' : undefined"
|
||||||
:rel="isExternal ? 'noreferrer' : undefined"
|
:rel="isExternal ? 'noreferrer' : undefined"
|
||||||
@ -50,7 +49,7 @@ function linkTo(e: Event) {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.VPButton {
|
.vp-button {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -60,76 +59,76 @@ function linkTo(e: Event) {
|
|||||||
transition-property: border, color, background-color;
|
transition-property: border, color, background-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton:active {
|
.vp-button:active {
|
||||||
transition:
|
transition:
|
||||||
color 0.1s,
|
color 0.1s,
|
||||||
border-color 0.1s,
|
border-color 0.1s,
|
||||||
background-color 0.1s;
|
background-color 0.1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.medium {
|
.vp-button.medium {
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 38px;
|
line-height: 38px;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.big {
|
.vp-button.big {
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.brand {
|
.vp-button.brand {
|
||||||
color: var(--vp-button-brand-text);
|
color: var(--vp-button-brand-text);
|
||||||
background-color: var(--vp-button-brand-bg);
|
background-color: var(--vp-button-brand-bg);
|
||||||
border-color: var(--vp-button-brand-border);
|
border-color: var(--vp-button-brand-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.brand:hover {
|
.vp-button.brand:hover {
|
||||||
color: var(--vp-button-brand-hover-text);
|
color: var(--vp-button-brand-hover-text);
|
||||||
background-color: var(--vp-button-brand-hover-bg);
|
background-color: var(--vp-button-brand-hover-bg);
|
||||||
border-color: var(--vp-button-brand-hover-border);
|
border-color: var(--vp-button-brand-hover-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.brand:active {
|
.vp-button.brand:active {
|
||||||
color: var(--vp-button-brand-active-text);
|
color: var(--vp-button-brand-active-text);
|
||||||
background-color: var(--vp-button-brand-active-bg);
|
background-color: var(--vp-button-brand-active-bg);
|
||||||
border-color: var(--vp-button-brand-active-border);
|
border-color: var(--vp-button-brand-active-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.alt {
|
.vp-button.alt {
|
||||||
color: var(--vp-button-alt-text);
|
color: var(--vp-button-alt-text);
|
||||||
background-color: var(--vp-button-alt-bg);
|
background-color: var(--vp-button-alt-bg);
|
||||||
border-color: var(--vp-button-alt-border);
|
border-color: var(--vp-button-alt-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.alt:hover {
|
.vp-button.alt:hover {
|
||||||
color: var(--vp-button-alt-hover-text);
|
color: var(--vp-button-alt-hover-text);
|
||||||
background-color: var(--vp-button-alt-hover-bg);
|
background-color: var(--vp-button-alt-hover-bg);
|
||||||
border-color: var(--vp-button-alt-hover-border);
|
border-color: var(--vp-button-alt-hover-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.alt:active {
|
.vp-button.alt:active {
|
||||||
color: var(--vp-button-alt-active-text);
|
color: var(--vp-button-alt-active-text);
|
||||||
background-color: var(--vp-button-alt-active-bg);
|
background-color: var(--vp-button-alt-active-bg);
|
||||||
border-color: var(--vp-button-alt-active-border);
|
border-color: var(--vp-button-alt-active-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.sponsor {
|
.vp-button.sponsor {
|
||||||
color: var(--vp-button-sponsor-text);
|
color: var(--vp-button-sponsor-text);
|
||||||
background-color: var(--vp-button-sponsor-bg);
|
background-color: var(--vp-button-sponsor-bg);
|
||||||
border-color: var(--vp-button-sponsor-border);
|
border-color: var(--vp-button-sponsor-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.sponsor:hover {
|
.vp-button.sponsor:hover {
|
||||||
color: var(--vp-button-sponsor-hover-text);
|
color: var(--vp-button-sponsor-hover-text);
|
||||||
background-color: var(--vp-button-sponsor-hover-bg);
|
background-color: var(--vp-button-sponsor-hover-bg);
|
||||||
border-color: var(--vp-button-sponsor-hover-border);
|
border-color: var(--vp-button-sponsor-hover-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.VPButton.sponsor:active {
|
.vp-button.sponsor:active {
|
||||||
color: var(--vp-button-sponsor-active-text);
|
color: var(--vp-button-sponsor-active-text);
|
||||||
background-color: var(--vp-button-sponsor-active-bg);
|
background-color: var(--vp-button-sponsor-active-bg);
|
||||||
border-color: var(--vp-button-sponsor-active-border);
|
border-color: var(--vp-button-sponsor-active-border);
|
||||||
126
theme/src/client/components/VPContent.vue
Normal file
126
theme/src/client/components/VPContent.vue
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useData, useSidebar } from '../composables/index.js'
|
||||||
|
import Blog from './Blog/Blog.vue'
|
||||||
|
import VPDoc from './VPDoc.vue'
|
||||||
|
import VPPage from './VPPage.vue'
|
||||||
|
import Home from './Home/Home.vue'
|
||||||
|
import Friends from './Friends.vue'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
isNotFound?: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { hasSidebar } = useSidebar()
|
||||||
|
const { frontmatter, page } = useData()
|
||||||
|
|
||||||
|
const isBlogLayout = computed(() => {
|
||||||
|
const { type } = page.value
|
||||||
|
return type === 'blog' || type === 'blog-archives' || type === 'blog-tags'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
id="VPContent"
|
||||||
|
vp-content
|
||||||
|
class="vp-content" :class="{
|
||||||
|
'has-sidebar': hasSidebar && !props.isNotFound,
|
||||||
|
'is-home': frontmatter.pageLayout === 'home',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Blog v-if="isBlogLayout" />
|
||||||
|
|
||||||
|
<VPPage v-else-if="frontmatter.pageLayout === 'page'">
|
||||||
|
<template #page-top>
|
||||||
|
<slot name="page-top" />
|
||||||
|
</template>
|
||||||
|
<template #page-bottom>
|
||||||
|
<slot name="page-bottom" />
|
||||||
|
</template>
|
||||||
|
</VPPage>
|
||||||
|
|
||||||
|
<Friends v-else-if="frontmatter.pageLayout === 'friends'" />
|
||||||
|
|
||||||
|
<Home v-else-if="frontmatter.pageLayout === 'home'" />
|
||||||
|
|
||||||
|
<component
|
||||||
|
:is="frontmatter.pageLayout"
|
||||||
|
v-else-if="frontmatter.pageLayout && frontmatter.pageLayout !== 'doc'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<VPDoc v-else>
|
||||||
|
<template #doc-top>
|
||||||
|
<slot name="doc-top" />
|
||||||
|
</template>
|
||||||
|
<template #doc-bottom>
|
||||||
|
<slot name="doc-bottom" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #doc-footer-before>
|
||||||
|
<slot name="doc-footer-before" />
|
||||||
|
</template>
|
||||||
|
<template #doc-before>
|
||||||
|
<slot name="doc-before" />
|
||||||
|
</template>
|
||||||
|
<template #doc-after>
|
||||||
|
<slot name="doc-after" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #aside-top>
|
||||||
|
<slot name="aside-top" />
|
||||||
|
</template>
|
||||||
|
<template #aside-outline-before>
|
||||||
|
<slot name="aside-outline-before" />
|
||||||
|
</template>
|
||||||
|
<template #aside-outline-after>
|
||||||
|
<slot name="aside-outline-after" />
|
||||||
|
</template>
|
||||||
|
<template #aside-ads-before>
|
||||||
|
<slot name="aside-ads-before" />
|
||||||
|
</template>
|
||||||
|
<template #aside-ads-after>
|
||||||
|
<slot name="aside-ads-after" />
|
||||||
|
</template>
|
||||||
|
<template #aside-bottom>
|
||||||
|
<slot name="aside-bottom" />
|
||||||
|
</template>
|
||||||
|
</VPDoc>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.vp-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin: var(--vp-layout-top-height, 0) auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-content.is-home {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-content.has-sidebar {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.vp-content {
|
||||||
|
padding-top: var(--vp-nav-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-content.has-sidebar {
|
||||||
|
padding-left: var(--vp-sidebar-width);
|
||||||
|
margin: var(--vp-layout-top-height, 0) 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1440px) {
|
||||||
|
.vp-content.has-sidebar {
|
||||||
|
padding-right: calc((100vw - var(--vp-layout-max-width)) / 2);
|
||||||
|
padding-left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
275
theme/src/client/components/VPDoc.vue
Normal file
275
theme/src/client/components/VPDoc.vue
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vuepress/client'
|
||||||
|
import { useData } from '../composables/data.js'
|
||||||
|
import { useSidebar } from '../composables/sidebar.js'
|
||||||
|
import { usePageEncrypt } from '../composables/encrypt.js'
|
||||||
|
import TransitionFadeSlideY from './TransitionFadeSlideY.vue'
|
||||||
|
import VPDocAside from './VPDocAside.vue'
|
||||||
|
import VPDocFooter from './VPDocFooter.vue'
|
||||||
|
import VPEncryptPage from './VPEncryptPage.vue'
|
||||||
|
import VPDocMeta from './VPDocMeta.vue'
|
||||||
|
|
||||||
|
const { page, theme, frontmatter, isDark } = useData()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { hasSidebar, hasAside, leftAside } = useSidebar()
|
||||||
|
const { isPageDecrypted } = usePageEncrypt()
|
||||||
|
|
||||||
|
const hasComments = computed(() => {
|
||||||
|
return page.value.frontmatter.comments !== false
|
||||||
|
})
|
||||||
|
|
||||||
|
const enableAside = computed(() => {
|
||||||
|
if (page.value.isBlogPost)
|
||||||
|
return hasAside.value && isPageDecrypted.value && page.value.headers.length
|
||||||
|
|
||||||
|
return hasAside.value && isPageDecrypted.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const pageName = computed(() =>
|
||||||
|
route.path.replace(/[./]+/g, '_').replace(/_html$/, ''),
|
||||||
|
)
|
||||||
|
const enabledExternalLinkIcon = computed(
|
||||||
|
() =>
|
||||||
|
theme.value.externalLinkIcon
|
||||||
|
&& frontmatter.value.externalLinkIcon !== false,
|
||||||
|
)
|
||||||
|
|
||||||
|
const asideEl = ref<HTMLElement>()
|
||||||
|
watch(
|
||||||
|
() => route.hash,
|
||||||
|
hash =>
|
||||||
|
nextTick(() => {
|
||||||
|
if (!asideEl.value)
|
||||||
|
return
|
||||||
|
const activeItem = asideEl.value.querySelector(
|
||||||
|
`.outline-link[href="${hash}"]`,
|
||||||
|
)
|
||||||
|
if (!activeItem || !hash) {
|
||||||
|
asideEl.value.scrollTop = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { top: navTop, height: navHeight }
|
||||||
|
= asideEl.value.getBoundingClientRect()
|
||||||
|
const { top: activeTop, height: activeHeight }
|
||||||
|
= activeItem.getBoundingClientRect()
|
||||||
|
|
||||||
|
if (activeTop < navTop || activeTop + activeHeight > navTop + navHeight)
|
||||||
|
activeItem.scrollIntoView({ block: 'center' })
|
||||||
|
}),
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TransitionFadeSlideY>
|
||||||
|
<div
|
||||||
|
:key="page.path" class="vp-doc-container" :class="{
|
||||||
|
'has-sidebar': hasSidebar,
|
||||||
|
'has-aside': hasAside,
|
||||||
|
'is-blog': page.isBlogPost,
|
||||||
|
'with-encrypt': !isPageDecrypted,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<slot name="doc-top" />
|
||||||
|
<div class="container">
|
||||||
|
<div v-if="enableAside" class="aside" :class="{ 'left-aside': leftAside }">
|
||||||
|
<div class="aside-curtain" />
|
||||||
|
<div ref="asideEl" class="aside-container">
|
||||||
|
<div class="aside-content">
|
||||||
|
<VPDocAside>
|
||||||
|
<template #aside-top>
|
||||||
|
<slot name="aside-top" />
|
||||||
|
</template>
|
||||||
|
<template #aside-bottom>
|
||||||
|
<slot name="aside-bottom" />
|
||||||
|
</template>
|
||||||
|
<template #aside-outline-before>
|
||||||
|
<slot name="aside-outline-before" />
|
||||||
|
</template>
|
||||||
|
<template #aside-outline-after>
|
||||||
|
<slot name="aside-outline-after" />
|
||||||
|
</template>
|
||||||
|
<template #aside-ads-before>
|
||||||
|
<slot name="aside-ads-before" />
|
||||||
|
</template>
|
||||||
|
<template #aside-ads-after>
|
||||||
|
<slot name="aside-ads-after" />
|
||||||
|
</template>
|
||||||
|
</VPDocAside>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="content-container">
|
||||||
|
<slot name="doc-before" />
|
||||||
|
<main class="main">
|
||||||
|
<VPDocMeta />
|
||||||
|
<VPEncryptPage v-if="!isPageDecrypted" />
|
||||||
|
<Content
|
||||||
|
v-else class="vp-doc plume-content"
|
||||||
|
:class="[pageName, enabledExternalLinkIcon && 'external-link-icon-enabled']"
|
||||||
|
/>
|
||||||
|
</main>
|
||||||
|
<VPDocFooter>
|
||||||
|
<template #doc-footer-before>
|
||||||
|
<slot name="doc-footer-before" />
|
||||||
|
</template>
|
||||||
|
</VPDocFooter>
|
||||||
|
<PageComment v-if="hasComments" :darkmode="isDark" />
|
||||||
|
<slot name="doc-after" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TransitionFadeSlideY>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.vp-doc-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 32px 24px 96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc-container.with-encrypt {
|
||||||
|
padding: 32px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.vp-doc-container {
|
||||||
|
padding: 48px 32px 128px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.vp-doc-container {
|
||||||
|
padding: 48px 32px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc-container:not(.has-sidebar) .container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
max-width: 992px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc-container:not(.has-sidebar) .content {
|
||||||
|
max-width: 752px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.vp-doc-container .container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc-container .aside {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1440px) {
|
||||||
|
.vp-doc-container:not(.has-sidebar) .content {
|
||||||
|
max-width: 784px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc-container:not(.has-sidebar) .container {
|
||||||
|
max-width: 1104px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
display: none;
|
||||||
|
flex-grow: 1;
|
||||||
|
order: 2;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 256px;
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-aside {
|
||||||
|
order: 1;
|
||||||
|
padding-right: 32px;
|
||||||
|
padding-left: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside-container {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
min-height: calc(100vh - var(--vp-footer-height, 0px));
|
||||||
|
max-height: 100vh;
|
||||||
|
padding-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 32px);
|
||||||
|
margin-top: calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1 - 32px);
|
||||||
|
overflow: hidden auto;
|
||||||
|
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside-container::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property --vp-aside-curtain-bg {
|
||||||
|
inherits: false;
|
||||||
|
initial-value: #fff;
|
||||||
|
syntax: "<color>";
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside-curtain {
|
||||||
|
--vp-aside-curtain-bg: var(--vp-c-bg);
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10;
|
||||||
|
width: 224px;
|
||||||
|
height: 32px;
|
||||||
|
background: linear-gradient(transparent, var(--vp-aside-curtain-bg) 70%);
|
||||||
|
transition: --vp-aside-curtain-bg var(--t-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aside-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px));
|
||||||
|
padding-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.content {
|
||||||
|
padding: 0 32px 128px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.content {
|
||||||
|
order: 1;
|
||||||
|
min-width: 640px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-doc-container.has-aside .content-container {
|
||||||
|
max-width: 688px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
29
theme/src/client/components/VPDocAside.vue
Normal file
29
theme/src/client/components/VPDocAside.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import VPDocAsideOutline from './VPDocAsideOutline.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="vp-doc-aside">
|
||||||
|
<slot name="aside-top" />
|
||||||
|
|
||||||
|
<slot name="aside-outline-before" />
|
||||||
|
<VPDocAsideOutline />
|
||||||
|
<slot name="aside-outline-after" />
|
||||||
|
|
||||||
|
<div class="spacer" />
|
||||||
|
|
||||||
|
<slot name="aside-bottom" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.vp-doc-aside {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -3,7 +3,7 @@ import { computed, ref } from 'vue'
|
|||||||
import { onContentUpdated } from '@vuepress-plume/plugin-content-update/client'
|
import { onContentUpdated } from '@vuepress-plume/plugin-content-update/client'
|
||||||
import { type MenuItem, getHeaders, useActiveAnchor } from '../composables/outline.js'
|
import { type MenuItem, getHeaders, useActiveAnchor } from '../composables/outline.js'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import PageAsideItem from './PageAsideItem.vue'
|
import VPDocOutlineItem from './VPDocOutlineItem.vue'
|
||||||
|
|
||||||
const { theme, frontmatter } = useData()
|
const { theme, frontmatter } = useData()
|
||||||
|
|
||||||
@ -25,46 +25,37 @@ function handlePrint() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="page-aside">
|
<nav
|
||||||
<div
|
ref="container"
|
||||||
ref="container"
|
aria-labelledby="doc-outline-aria-label"
|
||||||
class="page-aside-outline"
|
class="vp-doc-aside-outline"
|
||||||
:class="{ 'has-outline': hasOutline }"
|
:class="{ 'has-outline': hasOutline }"
|
||||||
>
|
role="navigation"
|
||||||
<div class="content">
|
>
|
||||||
<div ref="marker" class="outline-marker" />
|
<div class="content">
|
||||||
|
<div ref="marker" class="outline-marker" />
|
||||||
|
|
||||||
<div class="outline-title">
|
<div
|
||||||
<span>{{ theme.outlineLabel || 'On this page' }}</span>
|
id="doc-outline-aria-label"
|
||||||
<span class="vpi-print icon" @click="handlePrint" />
|
aria-level="2"
|
||||||
</div>
|
class="outline-title"
|
||||||
|
role="heading"
|
||||||
<nav aria-labelledby="doc-outline-aria-label">
|
>
|
||||||
<span id="doc-outline-aria-label" class="visually-hidden">
|
<span>{{ theme.outlineLabel || 'On this page' }}</span>
|
||||||
Table of Contents for current page
|
<span class="vpi-print icon" @click="handlePrint" />
|
||||||
</span>
|
|
||||||
<PageAsideItem
|
|
||||||
:headers="headers"
|
|
||||||
:root="true"
|
|
||||||
/>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<VPDocOutlineItem :headers="headers" :root="true" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page-aside {
|
.vp-doc-aside-outline {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-aside-outline {
|
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-aside-outline.has-outline {
|
.vp-doc-aside-outline.has-outline {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ import {
|
|||||||
usePageNav,
|
usePageNav,
|
||||||
} from '../composables/page.js'
|
} from '../composables/page.js'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import AutoLink from './AutoLink.vue'
|
import VPLink from './VPLink.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
const editNavLink = useEditNavLink()
|
const editNavLink = useEditNavLink()
|
||||||
@ -21,13 +21,15 @@ const showFooter = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<footer v-if="showFooter" class="page-footer">
|
<footer v-if="showFooter" class="vp-doc-footer">
|
||||||
|
<slot name="doc-footer-before" />
|
||||||
|
|
||||||
<div v-if="editNavLink || lastUpdated" class="edit-info">
|
<div v-if="editNavLink || lastUpdated" class="edit-info">
|
||||||
<div v-if="editNavLink" class="edit-link">
|
<div v-if="editNavLink" class="edit-link">
|
||||||
<AutoLink class="edit-link-button" :href="editNavLink.link" :no-icon="true">
|
<VPLink class="edit-link-button" :href="editNavLink.link" :no-icon="true">
|
||||||
<span class="vpi-square-pen edit-link-icon" aria-label="edit icon" />
|
<span class="vpi-square-pen edit-link-icon" aria-label="edit icon" />
|
||||||
{{ editNavLink.text }}
|
{{ editNavLink.text }}
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="lastUpdated" class="last-updated">
|
<div v-if="lastUpdated" class="last-updated">
|
||||||
@ -56,23 +58,23 @@ const showFooter = computed(() => {
|
|||||||
|
|
||||||
<nav v-if="prev?.link || next?.link" class="prev-next">
|
<nav v-if="prev?.link || next?.link" class="prev-next">
|
||||||
<div class="pager">
|
<div class="pager">
|
||||||
<AutoLink v-if="prev?.link" class="pager-link prev" :href="prev.link">
|
<VPLink v-if="prev?.link" class="pager-link prev" :href="prev.link">
|
||||||
<span class="desc" v-html="theme.prevPageLabel || 'Previous page'" />
|
<span class="desc" v-html="theme.prevPageLabel || 'Previous page'" />
|
||||||
<span class="title" v-html="prev.text" />
|
<span class="title" v-html="prev.text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
<div class="pager">
|
<div class="pager">
|
||||||
<AutoLink v-if="next?.link" class="pager-link next" :href="next.link">
|
<VPLink v-if="next?.link" class="pager-link next" :href="next.link">
|
||||||
<span class="desc" v-html="theme.nextPageLabel || 'Next page'" />
|
<span class="desc" v-html="theme.nextPageLabel || 'Next page'" />
|
||||||
<span class="title" v-html="next.text" />
|
<span class="title" v-html="next.text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page-footer {
|
.vp-doc-footer {
|
||||||
margin-top: 96px;
|
margin-top: 96px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="page.isBlogPost && categoryList.length"
|
v-if="page.isBlogPost && categoryList.length"
|
||||||
class="page-category-wrapper"
|
class="vp-doc-category"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="({ type, name }, index) in categoryList"
|
v-for="({ type, name }, index) in categoryList"
|
||||||
@ -47,10 +47,10 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
<span v-if="index !== categoryList.length - 1" class="dot">›</span>
|
<span v-if="index !== categoryList.length - 1" class="dot">›</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="page-title" :class="{ padding: !hasMeta }">
|
<h1 class="vp-doc-title" :class="{ padding: !hasMeta }">
|
||||||
{{ page.title }}
|
{{ page.title }}
|
||||||
</h1>
|
</h1>
|
||||||
<div v-if="hasMeta" class="page-meta-wrapper">
|
<div v-if="hasMeta" class="vp-doc-meta">
|
||||||
<p v-if="matter.author" class="author">
|
<p v-if="matter.author" class="author">
|
||||||
<span class="icon vpi-user" />
|
<span class="icon vpi-user" />
|
||||||
<span>{{ matter.author }}</span>
|
<span>{{ matter.author }}</span>
|
||||||
@ -78,7 +78,7 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.page-category-wrapper {
|
.vp-doc-category {
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -87,21 +87,21 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
transition: border-left var(--t-color);
|
transition: border-left var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-category-wrapper .category {
|
.vp-doc-category .category {
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-category-wrapper .category:hover {
|
.vp-doc-category .category:hover {
|
||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-category-wrapper .dot {
|
.vp-doc-category .dot {
|
||||||
margin: 0 0.2rem;
|
margin: 0 0.2rem;
|
||||||
color: var(--vp-c-text-3);
|
color: var(--vp-c-text-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title {
|
.vp-doc-title {
|
||||||
margin-bottom: 0.7rem;
|
margin-bottom: 0.7rem;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@ -110,11 +110,11 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-title.padding {
|
.vp-doc-title.padding {
|
||||||
padding-bottom: 4rem;
|
padding-bottom: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper {
|
.vp-doc-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -127,25 +127,25 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
transition: color var(--t-color), border-bottom var(--t-color);
|
transition: color var(--t-color), border-bottom var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper p {
|
.vp-doc-meta p {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .icon {
|
.vp-doc-meta .icon {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin-right: 0.3rem;
|
margin-right: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .author .icon,
|
.vp-doc-meta .author .icon,
|
||||||
.page-meta-wrapper .author span {
|
.vp-doc-meta .author span {
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .tag {
|
.vp-doc-meta .tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 3px 5px;
|
padding: 3px 5px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
@ -156,26 +156,26 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .tag:last-of-type {
|
.vp-doc-meta .tag:last-of-type {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .reading-time span {
|
.vp-doc-meta .reading-time span {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .reading-time span:last-of-type {
|
.vp-doc-meta .reading-time span:last-of-type {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-meta-wrapper .create-time {
|
.vp-doc-meta .create-time {
|
||||||
min-width: 110px;
|
min-width: 110px;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.page-meta-wrapper .create-time {
|
.vp-doc-meta .create-time {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
}
|
}
|
||||||
@ -18,9 +18,11 @@ function handleClick({ target: el }: Event) {
|
|||||||
<template>
|
<template>
|
||||||
<ul :class="root ? 'root' : 'nested'">
|
<ul :class="root ? 'root' : 'nested'">
|
||||||
<li v-for="{ children, link, title } in headers" :key="link">
|
<li v-for="{ children, link, title } in headers" :key="link">
|
||||||
<a class="outline-link" :href="link" @click="handleClick">{{ title }}</a>
|
<a
|
||||||
|
class="outline-link" :href="link" @click="handleClick"
|
||||||
|
>{{ title }}</a>
|
||||||
<template v-if="children?.length">
|
<template v-if="children?.length">
|
||||||
<PageAsideItem :headers="children" />
|
<VPDocOutlineItem :headers="children" />
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -2,8 +2,8 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import { useGlobalEncrypt } from '../composables/encrypt.js'
|
import { useGlobalEncrypt } from '../composables/encrypt.js'
|
||||||
import VFooter from './VFooter.vue'
|
import VPFooter from './VPFooter.vue'
|
||||||
import EncryptForm from './EncryptForm.vue'
|
import VPEncryptForm from './VPEncryptForm.vue'
|
||||||
|
|
||||||
const { theme, site } = useData()
|
const { theme, site } = useData()
|
||||||
const { compareGlobal } = useGlobalEncrypt()
|
const { compareGlobal } = useGlobalEncrypt()
|
||||||
@ -23,10 +23,10 @@ const title = computed(() => avatar.value?.name || site.value.title)
|
|||||||
{{ title }}
|
{{ title }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<EncryptForm :compare="compareGlobal" :info="theme.encryptGlobalText" />
|
<VPEncryptForm :compare="compareGlobal" :info="theme.encryptGlobalText" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<VFooter />
|
<VPFooter />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePageEncrypt } from '../composables/encrypt.js'
|
import { usePageEncrypt } from '../composables/encrypt.js'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import EncryptForm from './EncryptForm.vue'
|
import VPEncryptForm from './VPEncryptForm.vue'
|
||||||
|
|
||||||
const { theme } = useData()
|
const { theme } = useData()
|
||||||
const { comparePage } = usePageEncrypt()
|
const { comparePage } = usePageEncrypt()
|
||||||
@ -12,7 +12,7 @@ const { comparePage } = usePageEncrypt()
|
|||||||
<div class="logo">
|
<div class="logo">
|
||||||
<span class="vpi-lock icon-lock-head" />
|
<span class="vpi-lock icon-lock-head" />
|
||||||
</div>
|
</div>
|
||||||
<EncryptForm :compare="comparePage" :info="theme.encryptPageText" />
|
<VPEncryptForm :compare="comparePage" :info="theme.encryptPageText" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ onMounted(() => {
|
|||||||
<footer
|
<footer
|
||||||
v-if="theme.footer"
|
v-if="theme.footer"
|
||||||
ref="footer"
|
ref="footer"
|
||||||
class="plume-footer"
|
class="vp-footer"
|
||||||
:class="{ 'has-sidebar': hasSidebar }"
|
:class="{ 'has-sidebar': hasSidebar }"
|
||||||
>
|
>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -40,7 +40,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.plume-footer {
|
.vp-footer {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: var(--vp-z-index-footer);
|
z-index: var(--vp-z-index-footer);
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
@ -48,16 +48,16 @@ onMounted(() => {
|
|||||||
transition: all var(--t-color);
|
transition: all var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-no-border .plume-footer {
|
.footer-no-border .vp-footer {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.plume-footer p {
|
.vp-footer p {
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.plume-footer :deep(a) {
|
.vp-footer :deep(a) {
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
text-underline-offset: 2px;
|
text-underline-offset: 2px;
|
||||||
@ -66,18 +66,24 @@ onMounted(() => {
|
|||||||
text-underline-offset var(--t-color);
|
text-underline-offset var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.plume-footer :deep(a:hover) {
|
.vp-footer :deep(a:hover) {
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
text-underline-offset: 4px;
|
text-underline-offset: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1440px) {
|
@media (min-width: 960px) {
|
||||||
.plume-footer {
|
.vp-footer.has-sidebar {
|
||||||
padding: 24px;
|
margin-left: var(--vp-sidebar-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
.plume-footer.has-sidebar {
|
.vp-footer.vp-footer.has-sidebar .container {
|
||||||
margin-right: calc(0px - ((100vw - var(--vp-layout-max-width)) / 2));
|
margin-left: calc(0px - var(--vp-sidebar-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1440px) {
|
||||||
|
.vp-footer {
|
||||||
|
padding: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,13 +24,13 @@ export default {
|
|||||||
:alt="alt ?? (typeof image === 'string' ? '' : image.alt || '')"
|
:alt="alt ?? (typeof image === 'string' ? '' : image.alt || '')"
|
||||||
>
|
>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<VImage
|
<VPImage
|
||||||
class="dark"
|
class="dark"
|
||||||
:image="image.dark"
|
:image="image.dark"
|
||||||
:alt="image.alt"
|
:alt="image.alt"
|
||||||
v-bind="$attrs"
|
v-bind="$attrs"
|
||||||
/>
|
/>
|
||||||
<VImage
|
<VPImage
|
||||||
class="light"
|
class="light"
|
||||||
:image="image.light"
|
:image="image.light"
|
||||||
:alt="image.alt"
|
:alt="image.alt"
|
||||||
@ -39,7 +39,7 @@ function linkTo(e: Event) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Component
|
<Component
|
||||||
:is="tag" class="auto-link" :class="{ link }"
|
:is="tag" class="vp-link" :class="{ link }"
|
||||||
:href="link"
|
:href="link"
|
||||||
:target="target ?? (isExternal ? '_blank' : undefined)"
|
:target="target ?? (isExternal ? '_blank' : undefined)"
|
||||||
:rel="rel ?? (isExternal ? 'noreferrer' : undefined)"
|
:rel="rel ?? (isExternal ? 'noreferrer' : undefined)"
|
||||||
@ -61,7 +61,7 @@ function linkTo(e: Event) {
|
|||||||
transition: fill 0.25s;
|
transition: fill 0.25s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auto-link :deep(i) {
|
.vp-link :deep(i) {
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: inherit;
|
font-weight: inherit;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
@ -2,10 +2,10 @@
|
|||||||
import { useWindowScroll } from '@vueuse/core'
|
import { useWindowScroll } from '@vueuse/core'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { onContentUpdated } from '@vuepress-plume/plugin-content-update/client'
|
import { onContentUpdated } from '@vuepress-plume/plugin-content-update/client'
|
||||||
import { useSidebar } from '../../composables/sidebar.js'
|
import { useSidebar } from '../composables/sidebar.js'
|
||||||
import { type MenuItem, getHeaders } from '../../composables/outline.js'
|
import { type MenuItem, getHeaders } from '../composables/outline.js'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import LocalNavOutlineDropdown from './LocalNavOutlineDropdown.vue'
|
import VPLocalNavOutlineDropdown from './VPLocalNavOutlineDropdown.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
open: boolean
|
open: boolean
|
||||||
@ -41,7 +41,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
const classes = computed(() => {
|
const classes = computed(() => {
|
||||||
return {
|
return {
|
||||||
'local-nav': true,
|
'vp-local-nav': true,
|
||||||
'fixed': empty.value,
|
'fixed': empty.value,
|
||||||
'reached-top': y.value >= navHeight.value,
|
'reached-top': y.value >= navHeight.value,
|
||||||
'is-blog': page.value.isBlogPost,
|
'is-blog': page.value.isBlogPost,
|
||||||
@ -67,12 +67,12 @@ const showLocalNav = computed(() => {
|
|||||||
<span class="menu-text"> {{ theme.sidebarMenuLabel || 'Menu' }} </span>
|
<span class="menu-text"> {{ theme.sidebarMenuLabel || 'Menu' }} </span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<LocalNavOutlineDropdown v-if="showOutline" :headers="headers" :nav-height="navHeight" />
|
<VPLocalNavOutlineDropdown v-if="showOutline" :headers="headers" :nav-height="navHeight" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.local-nav {
|
.vp-local-nav {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
||||||
@ -93,43 +93,43 @@ const showLocalNav = computed(() => {
|
|||||||
border var(--t-color);
|
border var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav.fixed {
|
.vp-local-nav.fixed {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav.reached-top {
|
.vp-local-nav.reached-top {
|
||||||
border-top-color: transparent;
|
border-top-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
@media (min-width: 960px) {
|
||||||
.local-nav.is-blog {
|
.vp-local-nav.is-blog {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav {
|
.vp-local-nav {
|
||||||
top: var(--vp-nav-height);
|
top: var(--vp-nav-height);
|
||||||
width: calc(100% - var(--vp-sidebar-width));
|
width: calc(100% - var(--vp-sidebar-width));
|
||||||
margin-left: var(--vp-sidebar-width);
|
margin-left: var(--vp-sidebar-width);
|
||||||
border-top: none;
|
border-top: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav .menu {
|
.vp-local-nav .menu {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav.with-outline {
|
.vp-local-nav.with-outline {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1280px) {
|
@media (min-width: 1280px) {
|
||||||
.local-nav {
|
.vp-local-nav {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
.local-nav {
|
.vp-local-nav {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import { nextTick, ref, watch } from 'vue'
|
import { nextTick, ref, watch } from 'vue'
|
||||||
import type { MenuItem } from '../../composables/outline.js'
|
import type { MenuItem } from '../composables/outline.js'
|
||||||
import { useData } from '../../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import DocOutlineItem from './DocOutlineItem.vue'
|
import VPDocOutlineItem from './VPDocOutlineItem.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
headers: MenuItem[]
|
headers: MenuItem[]
|
||||||
@ -48,7 +48,7 @@ function scrollToTop() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="local-nav-outline-dropdown" :style="{ '--vp-vh': `${vh}px` }">
|
<div class="vp-local-nav-outline-dropdown" :style="{ '--vp-vh': `${vh}px` }">
|
||||||
<button v-if="headers.length > 0" ref="btn" :class="{ open }" @click="toggle">
|
<button v-if="headers.length > 0" ref="btn" :class="{ open }" @click="toggle">
|
||||||
{{ theme.outlineLabel || 'On this page' }}
|
{{ theme.outlineLabel || 'On this page' }}
|
||||||
<span class="vpi-chevron-right icon" />
|
<span class="vpi-chevron-right icon" />
|
||||||
@ -64,7 +64,7 @@ function scrollToTop() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="outline">
|
<div class="outline">
|
||||||
<DocOutlineItem :headers="headers" />
|
<VPDocOutlineItem :headers="headers" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
@ -72,11 +72,11 @@ function scrollToTop() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.local-nav-outline-dropdown {
|
.vp-local-nav-outline-dropdown {
|
||||||
padding: 12px 20px 11px;
|
padding: 12px 20px 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav-outline-dropdown button {
|
.vp-local-nav-outline-dropdown button {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -86,11 +86,11 @@ function scrollToTop() {
|
|||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav-outline-dropdown button:hover {
|
.vp-local-nav-outline-dropdown button:hover {
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.local-nav-outline-dropdown button.open {
|
.vp-local-nav-outline-dropdown button.open {
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
7
theme/src/client/components/VPPage.vue
Normal file
7
theme/src/client/components/VPPage.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<div class="vp-page">
|
||||||
|
<slot name="page-top" />
|
||||||
|
<Content class="plume-content" />
|
||||||
|
<slot name="page-bottom" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@ -4,7 +4,7 @@ import { onMounted, ref, watch } from 'vue'
|
|||||||
import { useRoutePath } from 'vuepress/client'
|
import { useRoutePath } from 'vuepress/client'
|
||||||
import { useSidebar } from '../composables/sidebar.js'
|
import { useSidebar } from '../composables/sidebar.js'
|
||||||
import { inBrowser } from '../utils/index.js'
|
import { inBrowser } from '../utils/index.js'
|
||||||
import SidebarItem from './SidebarItem.vue'
|
import VPSidebarItem from './VPSidebarItem.vue'
|
||||||
import TransitionFadeSlideY from './TransitionFadeSlideY.vue'
|
import TransitionFadeSlideY from './TransitionFadeSlideY.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -32,7 +32,7 @@ watch(
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const activeItem = document.querySelector(
|
const activeItem = document.querySelector(
|
||||||
`.sidebar-wrapper .auto-link[href*="${routePath.value}"]`,
|
`.vp-sidebar .vp-link[href*="${routePath.value}"]`,
|
||||||
)
|
)
|
||||||
if (!activeItem || !navEl.value)
|
if (!activeItem || !navEl.value)
|
||||||
return
|
return
|
||||||
@ -51,7 +51,7 @@ onMounted(() => {
|
|||||||
<aside
|
<aside
|
||||||
v-if="hasSidebar"
|
v-if="hasSidebar"
|
||||||
ref="navEl"
|
ref="navEl"
|
||||||
class="sidebar-wrapper"
|
class="vp-sidebar"
|
||||||
:class="{ open }"
|
:class="{ open }"
|
||||||
@click.stop
|
@click.stop
|
||||||
>
|
>
|
||||||
@ -74,7 +74,7 @@ onMounted(() => {
|
|||||||
:key="item.text"
|
:key="item.text"
|
||||||
class="group"
|
class="group"
|
||||||
>
|
>
|
||||||
<SidebarItem :item="item" :depth="0" />
|
<VPSidebarItem :item="item" :depth="0" />
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</TransitionFadeSlideY>
|
</TransitionFadeSlideY>
|
||||||
@ -83,7 +83,7 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sidebar-wrapper {
|
.vp-sidebar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: var(--vp-layout-top-height, 0);
|
top: var(--vp-layout-top-height, 0);
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -106,7 +106,7 @@ onMounted(() => {
|
|||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-wrapper.open {
|
.vp-sidebar.open {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition:
|
transition:
|
||||||
@ -115,12 +115,12 @@ onMounted(() => {
|
|||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .sidebar-wrapper {
|
.dark .vp-sidebar {
|
||||||
box-shadow: var(--vp-shadow-1);
|
box-shadow: var(--vp-shadow-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
@media (min-width: 960px) {
|
||||||
.sidebar-wrapper {
|
.vp-sidebar {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
width: var(--vp-sidebar-width);
|
width: var(--vp-sidebar-width);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@ -134,7 +134,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1440px) {
|
@media (min-width: 1440px) {
|
||||||
.sidebar-wrapper {
|
.vp-sidebar {
|
||||||
width:
|
width:
|
||||||
calc(
|
calc(
|
||||||
(100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) -
|
(100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) -
|
||||||
@ -2,8 +2,8 @@
|
|||||||
import type { NotesSidebarItem } from '@vuepress-plume/plugin-notes-data'
|
import type { NotesSidebarItem } from '@vuepress-plume/plugin-notes-data'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useSidebarControl } from '../composables/sidebar.js'
|
import { useSidebarControl } from '../composables/sidebar.js'
|
||||||
import AutoLink from './AutoLink.vue'
|
import VPLink from './VPLink.vue'
|
||||||
import VIcon from './VIcon.vue'
|
import VPIcon from './VPIcon.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
item: NotesSidebarItem
|
item: NotesSidebarItem
|
||||||
@ -56,7 +56,7 @@ function onCaretClick() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Component :is="sectionTag" class="sidebar-item" :class="classes">
|
<Component :is="sectionTag" class="vp-sidebar-item" :class="classes">
|
||||||
<div
|
<div
|
||||||
v-if="item.text"
|
v-if="item.text"
|
||||||
class="item"
|
class="item"
|
||||||
@ -69,16 +69,16 @@ function onCaretClick() {
|
|||||||
>
|
>
|
||||||
<div class="indicator" />
|
<div class="indicator" />
|
||||||
|
|
||||||
<VIcon v-if="item.icon" :name="item.icon" />
|
<VPIcon v-if="item.icon" :name="item.icon" />
|
||||||
|
|
||||||
<AutoLink
|
<VPLink
|
||||||
v-if="item.link"
|
v-if="item.link"
|
||||||
:tag="linkTag"
|
:tag="linkTag"
|
||||||
class="link"
|
class="link"
|
||||||
:href="item.link"
|
:href="item.link"
|
||||||
>
|
>
|
||||||
<Component :is="textTag" class="text" v-html="item.text" />
|
<Component :is="textTag" class="text" v-html="item.text" />
|
||||||
</AutoLink>
|
</VPLink>
|
||||||
|
|
||||||
<Component :is="textTag" v-else class="text" v-html="item.text" />
|
<Component :is="textTag" v-else class="text" v-html="item.text" />
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ function onCaretClick() {
|
|||||||
|
|
||||||
<div v-if="item.items && item.items.length" class="items">
|
<div v-if="item.items && item.items.length" class="items">
|
||||||
<template v-if="depth < 5">
|
<template v-if="depth < 5">
|
||||||
<SidebarItem
|
<VPSidebarItem
|
||||||
v-for="i in (item.items as NotesSidebarItem[])"
|
v-for="i in (item.items as NotesSidebarItem[])"
|
||||||
:key="i.text"
|
:key="i.text"
|
||||||
:item="i"
|
:item="i"
|
||||||
@ -109,11 +109,11 @@ function onCaretClick() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sidebar-item.level-0 {
|
.vp-sidebar-item.level-0 {
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.collapsed.level-0 {
|
.vp-sidebar-item.collapsed.level-0 {
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ function onCaretClick() {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.collapsible > .item {
|
.vp-sidebar-item.collapsible > .item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,10 +137,10 @@ function onCaretClick() {
|
|||||||
transition: background-color var(--t-color);
|
transition: background-color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-2.is-active > .item > .indicator,
|
.vp-sidebar-item.level-2.is-active > .item > .indicator,
|
||||||
.sidebar-item.level-3.is-active > .item > .indicator,
|
.vp-sidebar-item.level-3.is-active > .item > .indicator,
|
||||||
.sidebar-item.level-4.is-active > .item > .indicator,
|
.vp-sidebar-item.level-4.is-active > .item > .indicator,
|
||||||
.sidebar-item.level-5.is-active > .item > .indicator {
|
.vp-sidebar-item.level-5.is-active > .item > .indicator {
|
||||||
background-color: var(--vp-c-brand-1);
|
background-color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,50 +157,50 @@ function onCaretClick() {
|
|||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-0 .text {
|
.vp-sidebar-item.level-0 .text {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-1 .text,
|
.vp-sidebar-item.level-1 .text,
|
||||||
.sidebar-item.level-2 .text,
|
.vp-sidebar-item.level-2 .text,
|
||||||
.sidebar-item.level-3 .text,
|
.vp-sidebar-item.level-3 .text,
|
||||||
.sidebar-item.level-4 .text,
|
.vp-sidebar-item.level-4 .text,
|
||||||
.sidebar-item.level-5 .text {
|
.vp-sidebar-item.level-5 .text {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-0.has-active > .item > .text,
|
.vp-sidebar-item.level-0.has-active > .item > .text,
|
||||||
.sidebar-item.level-1.has-active > .item > .text,
|
.vp-sidebar-item.level-1.has-active > .item > .text,
|
||||||
.sidebar-item.level-2.has-active > .item > .text,
|
.vp-sidebar-item.level-2.has-active > .item > .text,
|
||||||
.sidebar-item.level-3.has-active > .item > .text,
|
.vp-sidebar-item.level-3.has-active > .item > .text,
|
||||||
.sidebar-item.level-4.has-active > .item > .text,
|
.vp-sidebar-item.level-4.has-active > .item > .text,
|
||||||
.sidebar-item.level-5.has-active > .item > .text,
|
.vp-sidebar-item.level-5.has-active > .item > .text,
|
||||||
.sidebar-item.level-0.has-active > .item > .link > .text,
|
.vp-sidebar-item.level-0.has-active > .item > .link > .text,
|
||||||
.sidebar-item.level-1.has-active > .item > .link > .text,
|
.vp-sidebar-item.level-1.has-active > .item > .link > .text,
|
||||||
.sidebar-item.level-2.has-active > .item > .link > .text,
|
.vp-sidebar-item.level-2.has-active > .item > .link > .text,
|
||||||
.sidebar-item.level-3.has-active > .item > .link > .text,
|
.vp-sidebar-item.level-3.has-active > .item > .link > .text,
|
||||||
.sidebar-item.level-4.has-active > .item > .link > .text,
|
.vp-sidebar-item.level-4.has-active > .item > .link > .text,
|
||||||
.sidebar-item.level-5.has-active > .item > .link > .text {
|
.vp-sidebar-item.level-5.has-active > .item > .link > .text {
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-0.is-active > .item .link > .text,
|
.vp-sidebar-item.level-0.is-active > .item .link > .text,
|
||||||
.sidebar-item.level-1.is-active > .item .link > .text,
|
.vp-sidebar-item.level-1.is-active > .item .link > .text,
|
||||||
.sidebar-item.level-2.is-active > .item .link > .text,
|
.vp-sidebar-item.level-2.is-active > .item .link > .text,
|
||||||
.sidebar-item.level-3.is-active > .item .link > .text,
|
.vp-sidebar-item.level-3.is-active > .item .link > .text,
|
||||||
.sidebar-item.level-4.is-active > .item .link > .text,
|
.vp-sidebar-item.level-4.is-active > .item .link > .text,
|
||||||
.sidebar-item.level-5.is-active > .item .link > .text {
|
.vp-sidebar-item.level-5.is-active > .item .link > .text {
|
||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-0.is-link > .item > .link:hover .text,
|
.vp-sidebar-item.level-0.is-link > .item > .link:hover .text,
|
||||||
.sidebar-item.level-1.is-link > .item > .link:hover .text,
|
.vp-sidebar-item.level-1.is-link > .item > .link:hover .text,
|
||||||
.sidebar-item.level-2.is-link > .item > .link:hover .text,
|
.vp-sidebar-item.level-2.is-link > .item > .link:hover .text,
|
||||||
.sidebar-item.level-3.is-link > .item > .link:hover .text,
|
.vp-sidebar-item.level-3.is-link > .item > .link:hover .text,
|
||||||
.sidebar-item.level-4.is-link > .item > .link:hover .text,
|
.vp-sidebar-item.level-4.is-link > .item > .link:hover .text,
|
||||||
.sidebar-item.level-5.is-link > .item > .link:hover .text {
|
.vp-sidebar-item.level-5.is-link > .item > .link:hover .text {
|
||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,21 +236,21 @@ function onCaretClick() {
|
|||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-0.is-active > .item > :deep(.vp-iconify),
|
.vp-sidebar-item.level-0.is-active > .item > :deep(.vp-iconify),
|
||||||
.sidebar-item.level-1.is-active > .item > :deep(.vp-iconify),
|
.vp-sidebar-item.level-1.is-active > .item > :deep(.vp-iconify),
|
||||||
.sidebar-item.level-2.is-active > .item > :deep(.vp-iconify),
|
.vp-sidebar-item.level-2.is-active > .item > :deep(.vp-iconify),
|
||||||
.sidebar-item.level-3.is-active > .item > :deep(.vp-iconify),
|
.vp-sidebar-item.level-3.is-active > .item > :deep(.vp-iconify),
|
||||||
.sidebar-item.level-4.is-active > .item > :deep(.vp-iconify),
|
.vp-sidebar-item.level-4.is-active > .item > :deep(.vp-iconify),
|
||||||
.sidebar-item.level-5.is-active > .item > :deep(.vp-iconify) {
|
.vp-sidebar-item.level-5.is-active > .item > :deep(.vp-iconify) {
|
||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-0.is-link > .item:hover :deep(.vp-iconify),
|
.vp-sidebar-item.level-0.is-link > .item:hover :deep(.vp-iconify),
|
||||||
.sidebar-item.level-1.is-link > .item:hover :deep(.vp-iconify),
|
.vp-sidebar-item.level-1.is-link > .item:hover :deep(.vp-iconify),
|
||||||
.sidebar-item.level-2.is-link > .item:hover :deep(.vp-iconify),
|
.vp-sidebar-item.level-2.is-link > .item:hover :deep(.vp-iconify),
|
||||||
.sidebar-item.level-3.is-link > .item:hover :deep(.vp-iconify),
|
.vp-sidebar-item.level-3.is-link > .item:hover :deep(.vp-iconify),
|
||||||
.sidebar-item.level-4.is-link > .item:hover :deep(.vp-iconify),
|
.vp-sidebar-item.level-4.is-link > .item:hover :deep(.vp-iconify),
|
||||||
.sidebar-item.level-5.is-link > .item:hover :deep(.vp-iconify) {
|
.vp-sidebar-item.level-5.is-link > .item:hover :deep(.vp-iconify) {
|
||||||
color: var(--vp-c-brand-1);
|
color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,21 +262,21 @@ function onCaretClick() {
|
|||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.collapsed .caret-icon {
|
.vp-sidebar-item.collapsed .caret-icon {
|
||||||
transform: rotate(0);
|
transform: rotate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.level-1 .items,
|
.vp-sidebar-item.level-1 .items,
|
||||||
.sidebar-item.level-2 .items,
|
.vp-sidebar-item.level-2 .items,
|
||||||
.sidebar-item.level-3 .items,
|
.vp-sidebar-item.level-3 .items,
|
||||||
.sidebar-item.level-4 .items,
|
.vp-sidebar-item.level-4 .items,
|
||||||
.sidebar-item.level-5 .items {
|
.vp-sidebar-item.level-5 .items {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
border-left: 1px solid var(--vp-c-divider);
|
border-left: 1px solid var(--vp-c-divider);
|
||||||
transition: border-left var(--t-color);
|
transition: border-left var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-item.collapsed .items {
|
.vp-sidebar-item.collapsed .items {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -32,7 +32,7 @@ function focusOnTargetAnchor({ target }: Event) {
|
|||||||
<template>
|
<template>
|
||||||
<span ref="backToTop" tabindex="-1" />
|
<span ref="backToTop" tabindex="-1" />
|
||||||
<a
|
<a
|
||||||
href="#LayoutContent"
|
href="#VPContent"
|
||||||
class="skip-link visually-hidden"
|
class="skip-link visually-hidden"
|
||||||
@click="focusOnTargetAnchor"
|
@click="focusOnTargetAnchor"
|
||||||
>
|
>
|
||||||
@ -17,17 +17,15 @@ const svg = computed(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
class="social-link"
|
class="vp-social-link"
|
||||||
:href="link"
|
:href="link"
|
||||||
:aria-label="ariaLabel ?? (typeof icon === 'string' ? icon : '')"
|
:aria-label="ariaLabel ?? (typeof icon === 'string' ? icon : '')"
|
||||||
target="_blank"
|
target="_blank" rel="noopener" v-html="svg"
|
||||||
rel="noopener"
|
|
||||||
v-html="svg"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.social-link {
|
.vp-social-link {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -37,12 +35,12 @@ const svg = computed(() => {
|
|||||||
transition: color var(--t-color);
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.social-link:hover {
|
.vp-social-link:hover {
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.social-link > :deep(svg),
|
.vp-social-link > :deep(svg),
|
||||||
.social-link > :deep([class^="vpi-social-"]) {
|
.vp-social-link > :deep([class^="vpi-social-"]) {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
fill: currentcolor;
|
fill: currentcolor;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SocialLink as SocialLinkType } from '../../shared/index.js'
|
import type { SocialLink as SocialLinkType } from '../../shared/index.js'
|
||||||
import SocialLink from './SocialLink.vue'
|
import VPSocialLink from './VPSocialLink.vue'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
links: SocialLinkType[]
|
links: SocialLinkType[]
|
||||||
@ -8,8 +8,8 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="social-links">
|
<div class="vp-social-links">
|
||||||
<SocialLink
|
<VPSocialLink
|
||||||
v-for="{ link, icon } in links"
|
v-for="{ link, icon } in links"
|
||||||
:key="link"
|
:key="link"
|
||||||
:icon="icon"
|
:icon="icon"
|
||||||
@ -19,7 +19,7 @@ defineProps<{
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.social-links {
|
.vp-social-links {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<button class="switch-wrapper" type="button" role="switch">
|
<button class="vp-switch" type="button" role="switch">
|
||||||
<span class="check">
|
<span class="check">
|
||||||
<span v-if="$slots.default" class="icon">
|
<span v-if="$slots.default" class="icon">
|
||||||
<slot />
|
<slot />
|
||||||
@ -9,7 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.switch-wrapper {
|
.vp-switch {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@ -23,7 +23,7 @@
|
|||||||
background-color 0.25s ease;
|
background-color 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.switch-wrapper:hover {
|
.vp-switch:hover {
|
||||||
border-color: var(--vp-c-brand-1);
|
border-color: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, ref } from 'vue'
|
import { computed, inject, ref } from 'vue'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
import Switch from './Switch.vue'
|
import VPSwitch from './VPSwitch.vue'
|
||||||
|
|
||||||
const checked = ref(false)
|
const checked = ref(false)
|
||||||
const { theme, isDark } = useData()
|
const { theme, isDark } = useData()
|
||||||
@ -18,15 +18,15 @@ const switchTitle = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Switch
|
<VPSwitch
|
||||||
class="switch-appearance"
|
class="vp-switch-appearance"
|
||||||
:title="switchTitle"
|
:title="switchTitle"
|
||||||
:aria-checked="checked"
|
:aria-checked="checked"
|
||||||
@click="toggleAppearance"
|
@click="toggleAppearance"
|
||||||
>
|
>
|
||||||
<span class="vpi-sun sun" />
|
<span class="vpi-sun sun" />
|
||||||
<span class="vpi-moon moon" />
|
<span class="vpi-moon moon" />
|
||||||
</Switch>
|
</VPSwitch>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -46,7 +46,7 @@ const switchTitle = computed(() => {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .switch-appearance :deep(.check) {
|
.dark .vp-switch-appearance :deep(.check) {
|
||||||
/* rtl:ignore */
|
/* rtl:ignore */
|
||||||
transform: translateX(18px);
|
transform: translateX(18px);
|
||||||
}
|
}
|
||||||
@ -10,13 +10,13 @@ withDefaults(defineProps<Props>(), {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span class="badge-view" :class="type">
|
<span class="vp-badge" :class="type">
|
||||||
<slot>{{ text }}</slot>
|
<slot>{{ text }}</slot>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.badge-view {
|
.vp-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
@ -30,47 +30,47 @@ withDefaults(defineProps<Props>(), {
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 .badge-view {
|
h1 .vp-badge {
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 .badge-view {
|
h2 .vp-badge {
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 .badge-view {
|
h3 .vp-badge {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 .badge-view,
|
h4 .vp-badge,
|
||||||
h5 .badge-view,
|
h5 .vp-badge,
|
||||||
h6 .badge-view {
|
h6 .vp-badge {
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-view.info {
|
.vp-badge.info {
|
||||||
color: var(--vp-badge-info-text);
|
color: var(--vp-badge-info-text);
|
||||||
background-color: var(--vp-badge-info-bg);
|
background-color: var(--vp-badge-info-bg);
|
||||||
border-color: var(--vp-badge-info-border);
|
border-color: var(--vp-badge-info-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-view.tip {
|
.vp-badge.tip {
|
||||||
color: var(--vp-badge-tip-text);
|
color: var(--vp-badge-tip-text);
|
||||||
background-color: var(--vp-badge-tip-bg);
|
background-color: var(--vp-badge-tip-bg);
|
||||||
border-color: var(--vp-badge-tip-border);
|
border-color: var(--vp-badge-tip-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-view.warning {
|
.vp-badge.warning {
|
||||||
color: var(--vp-badge-warning-text);
|
color: var(--vp-badge-warning-text);
|
||||||
background-color: var(--vp-badge-warning-bg);
|
background-color: var(--vp-badge-warning-bg);
|
||||||
border-color: var(--vp-badge-warning-border);
|
border-color: var(--vp-badge-warning-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-view.danger {
|
.vp-badge.danger {
|
||||||
color: var(--vp-badge-danger-text);
|
color: var(--vp-badge-danger-text);
|
||||||
background-color: var(--vp-badge-danger-bg);
|
background-color: var(--vp-badge-danger-bg);
|
||||||
border-color: var(--vp-badge-danger-border);
|
border-color: var(--vp-badge-danger-border);
|
||||||
@ -66,7 +66,7 @@ function serializeHeader(h: Element): string {
|
|||||||
for (const node of Array.from(el?.childNodes ?? [])) {
|
for (const node of Array.from(el?.childNodes ?? [])) {
|
||||||
if (node.nodeType === 1) {
|
if (node.nodeType === 1) {
|
||||||
if (
|
if (
|
||||||
(node as Element).classList.contains('badge-view')
|
(node as Element).classList.contains('vp-badge')
|
||||||
|| (node as Element).classList.contains('ignore-header')
|
|| (node as Element).classList.contains('ignore-header')
|
||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -11,16 +11,15 @@ import type {
|
|||||||
PlumeThemePageFrontmatter,
|
PlumeThemePageFrontmatter,
|
||||||
} from '../../shared/index.js'
|
} from '../../shared/index.js'
|
||||||
import { useSidebar, useThemeLocaleData } from '../composables/index.js'
|
import { useSidebar, useThemeLocaleData } from '../composables/index.js'
|
||||||
|
import { useData } from '../composables/data.js'
|
||||||
import { resolveEditLink, resolveNavLink } from '../utils/index.js'
|
import { resolveEditLink, resolveNavLink } from '../utils/index.js'
|
||||||
|
|
||||||
export function useEditNavLink(): ComputedRef<null | NavItemWithLink> {
|
export function useEditNavLink(): ComputedRef<null | NavItemWithLink> {
|
||||||
const themeLocale = useThemeLocaleData()
|
const { theme, page, frontmatter } = useData()
|
||||||
const page = usePageData<PlumeThemePageData>()
|
|
||||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
|
||||||
|
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
const showEditLink
|
const showEditLink
|
||||||
= frontmatter.value.editLink ?? themeLocale.value.editLink ?? true
|
= frontmatter.value.editLink ?? theme.value.editLink ?? true
|
||||||
if (!showEditLink)
|
if (!showEditLink)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ export function useEditNavLink(): ComputedRef<null | NavItemWithLink> {
|
|||||||
docsBranch = 'main',
|
docsBranch = 'main',
|
||||||
docsDir = '',
|
docsDir = '',
|
||||||
editLinkText,
|
editLinkText,
|
||||||
} = themeLocale.value
|
} = theme.value
|
||||||
|
|
||||||
if (!docsRepo)
|
if (!docsRepo)
|
||||||
return null
|
return null
|
||||||
@ -40,7 +39,7 @@ export function useEditNavLink(): ComputedRef<null | NavItemWithLink> {
|
|||||||
docsDir,
|
docsDir,
|
||||||
filePathRelative: page.value.filePathRelative,
|
filePathRelative: page.value.filePathRelative,
|
||||||
editLinkPattern:
|
editLinkPattern:
|
||||||
frontmatter.value.editLinkPattern ?? themeLocale.value.editLinkPattern,
|
frontmatter.value.editLinkPattern ?? theme.value.editLinkPattern,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!editLink)
|
if (!editLink)
|
||||||
@ -54,9 +53,7 @@ export function useEditNavLink(): ComputedRef<null | NavItemWithLink> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function useLastUpdated() {
|
export function useLastUpdated() {
|
||||||
const theme = useThemeLocaleData()
|
const { theme, page, frontmatter } = useData()
|
||||||
const page = usePageData<PlumeThemePageData>()
|
|
||||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
|
||||||
const lang = usePageLang()
|
const lang = usePageLang()
|
||||||
|
|
||||||
const date = computed(() => page.value.git?.updatedTime ? new Date(page.value.git.updatedTime) : null)
|
const date = computed(() => page.value.git?.updatedTime ? new Date(page.value.git.updatedTime) : null)
|
||||||
@ -97,13 +94,11 @@ export function useLastUpdated() {
|
|||||||
export function useContributors(): ComputedRef<
|
export function useContributors(): ComputedRef<
|
||||||
null | Required<PlumeThemePageData['git']>['contributors']
|
null | Required<PlumeThemePageData['git']>['contributors']
|
||||||
> {
|
> {
|
||||||
const themeLocale = useThemeLocaleData()
|
const { theme, page, frontmatter } = useData()
|
||||||
const page = usePageData<PlumeThemePageData>()
|
|
||||||
const frontmatter = usePageFrontmatter<PlumeThemePageFrontmatter>()
|
|
||||||
|
|
||||||
return computed(() => {
|
return computed(() => {
|
||||||
const showContributors
|
const showContributors
|
||||||
= frontmatter.value.contributors ?? themeLocale.value.contributors ?? true
|
= frontmatter.value.contributors ?? theme.value.contributors ?? true
|
||||||
|
|
||||||
if (!showContributors)
|
if (!showContributors)
|
||||||
return null
|
return null
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export function getSidebarFirstLink(sidebar: NotesSidebarItem[]) {
|
|||||||
export function useSidebar() {
|
export function useSidebar() {
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const notesData = useNotesData()
|
const notesData = useNotesData()
|
||||||
const { frontmatter } = useData()
|
const { frontmatter, theme } = useData()
|
||||||
|
|
||||||
const is960 = useMediaQuery('(min-width: 960px)')
|
const is960 = useMediaQuery('(min-width: 960px)')
|
||||||
|
|
||||||
@ -79,14 +79,28 @@ export function useSidebar() {
|
|||||||
|
|
||||||
const hasSidebar = computed(() => {
|
const hasSidebar = computed(() => {
|
||||||
return (
|
return (
|
||||||
!frontmatter.value.home
|
frontmatter.value.pageLayout !== 'home'
|
||||||
&& sidebar.value.length > 0
|
&& sidebar.value.length > 0
|
||||||
&& frontmatter.value.sidebar !== false
|
&& frontmatter.value.sidebar !== false
|
||||||
|
&& frontmatter.value.layout !== 'NotFound'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const hasAside = computed(() => {
|
const hasAside = computed(() => {
|
||||||
return !frontmatter.value.home && frontmatter.value.aside !== false
|
if (frontmatter.value.pageLayout === 'home')
|
||||||
|
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 isSidebarEnabled = computed(() => hasSidebar.value && is960.value)
|
||||||
@ -112,6 +126,7 @@ export function useSidebar() {
|
|||||||
sidebar,
|
sidebar,
|
||||||
hasSidebar,
|
hasSidebar,
|
||||||
hasAside,
|
hasAside,
|
||||||
|
leftAside,
|
||||||
isSidebarEnabled,
|
isSidebarEnabled,
|
||||||
sidebarGroups,
|
sidebarGroups,
|
||||||
sidebarKey,
|
sidebarKey,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import './styles/index.css'
|
|||||||
import { defineClientConfig } from 'vuepress/client'
|
import { defineClientConfig } from 'vuepress/client'
|
||||||
import type { ClientConfig } from 'vuepress/client'
|
import type { ClientConfig } from 'vuepress/client'
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
import Badge from './components/global/Badge.vue'
|
import VPBadge from './components/global/VPBadge.vue'
|
||||||
import { setupDarkMode, setupWatermark, useScrollPromise } from './composables/index.js'
|
import { setupDarkMode, setupWatermark, useScrollPromise } from './composables/index.js'
|
||||||
import Layout from './layouts/Layout.vue'
|
import Layout from './layouts/Layout.vue'
|
||||||
import NotFound from './layouts/NotFound.vue'
|
import NotFound from './layouts/NotFound.vue'
|
||||||
@ -13,7 +13,8 @@ export default defineClientConfig({
|
|||||||
enhance({ app, router }) {
|
enhance({ app, router }) {
|
||||||
setupDarkMode(app)
|
setupDarkMode(app)
|
||||||
// global component
|
// global component
|
||||||
app.component('Badge', Badge)
|
app.component('Badge', VPBadge)
|
||||||
|
app.component('VPBadge', VPBadge) // alias
|
||||||
|
|
||||||
app.component('DocSearch', () => {
|
app.component('DocSearch', () => {
|
||||||
const SearchComponent
|
const SearchComponent
|
||||||
|
|||||||
@ -1,25 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePageData, useRoute } from 'vuepress/client'
|
import { useRoute } from 'vuepress/client'
|
||||||
import { computed, provide, watch } from 'vue'
|
import { watch } from 'vue'
|
||||||
import type { PlumeThemePageData } from '../../shared/index.js'
|
import VPBackdrop from '../components/VPBackdrop.vue'
|
||||||
import Backdrop from '../components/Backdrop.vue'
|
import VPContent from '../components/VPContent.vue'
|
||||||
import Blog from '../components/Blog/Blog.vue'
|
import VPLocalNav from '../components/VPLocalNav.vue'
|
||||||
import Friends from '../components/Friends.vue'
|
|
||||||
import Home from '../components/Home/Home.vue'
|
|
||||||
import LayoutContent from '../components/LayoutContent.vue'
|
|
||||||
import LocalNav from '../components/Nav/LocalNav.vue'
|
|
||||||
import Nav from '../components/Nav/index.vue'
|
import Nav from '../components/Nav/index.vue'
|
||||||
import Page from '../components/Page.vue'
|
import VPSidebar from '../components/VPSidebar.vue'
|
||||||
import Sidebar from '../components/Sidebar.vue'
|
import VPSkipLink from '../components/VPSkipLink.vue'
|
||||||
import SkipLink from '../components/SkipLink.vue'
|
import VPFooter from '../components/VPFooter.vue'
|
||||||
import VFooter from '../components/VFooter.vue'
|
import VPBackToTop from '../components/VPBackToTop.vue'
|
||||||
import BackToTop from '../components/BackToTop.vue'
|
import VPEncryptGlobal from '../components/VPEncryptGlobal.vue'
|
||||||
import EncryptGlobal from '../components/EncryptGlobal.vue'
|
import { useCloseSidebarOnEscape, useSidebar } from '../composables/sidebar.js'
|
||||||
import TransitionFadeSlideY from '../components/TransitionFadeSlideY.vue'
|
|
||||||
import { useCloseSidebarOnEscape, useSidebar } from '../composables/index.js'
|
|
||||||
import { useGlobalEncrypt, usePageEncrypt } from '../composables/encrypt.js'
|
import { useGlobalEncrypt, usePageEncrypt } from '../composables/encrypt.js'
|
||||||
|
import { useData } from '../composables/data.js'
|
||||||
const page = usePageData<PlumeThemePageData>()
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isSidebarOpen,
|
isOpen: isSidebarOpen,
|
||||||
@ -27,53 +20,76 @@ const {
|
|||||||
close: closeSidebar,
|
close: closeSidebar,
|
||||||
} = useSidebar()
|
} = useSidebar()
|
||||||
|
|
||||||
|
const { frontmatter } = useData()
|
||||||
const { isGlobalDecrypted } = useGlobalEncrypt()
|
const { isGlobalDecrypted } = useGlobalEncrypt()
|
||||||
const { isPageDecrypted } = usePageEncrypt()
|
const { isPageDecrypted } = usePageEncrypt()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
watch(() => route.path, closeSidebar)
|
watch(() => route.path, closeSidebar)
|
||||||
|
|
||||||
const isBlogLayout = computed(() => {
|
|
||||||
return (
|
|
||||||
page.value.type === 'blog'
|
|
||||||
|| page.value.type === 'blog-archives'
|
|
||||||
|| page.value.type === 'blog-tags'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
|
useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
|
||||||
|
|
||||||
provide('close-sidebar', closeSidebar)
|
|
||||||
provide('is-sidebar-open', isSidebarOpen)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="theme-plume">
|
<div
|
||||||
<EncryptGlobal v-if="!isGlobalDecrypted" />
|
v-if="frontmatter.pageLayout !== false && frontmatter.pageLayout !== 'custom'" class="theme-plume vp-layout"
|
||||||
|
:class="frontmatter.pageClass"
|
||||||
|
>
|
||||||
|
<VPEncryptGlobal v-if="!isGlobalDecrypted" />
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<SkipLink />
|
<VPSkipLink />
|
||||||
<Backdrop :show="isSidebarOpen" @click="closeSidebar" />
|
<VPBackdrop :show="isSidebarOpen" @click="closeSidebar" />
|
||||||
|
|
||||||
<Nav />
|
<Nav />
|
||||||
<LocalNav :open="isSidebarOpen" :show-outline="isPageDecrypted" @open-menu="openSidebar" />
|
<VPLocalNav :open="isSidebarOpen" :show-outline="isPageDecrypted" @open-menu="openSidebar" />
|
||||||
<Sidebar :open="isSidebarOpen" />
|
<VPSidebar :open="isSidebarOpen" />
|
||||||
<LayoutContent>
|
<slot name="custom-content">
|
||||||
<Home v-if="page.frontmatter.home" />
|
<VPContent>
|
||||||
<template v-else>
|
<template #page-top>
|
||||||
<TransitionFadeSlideY>
|
<slot name="page-top" />
|
||||||
<Friends v-if="page.frontmatter.friends" />
|
</template>
|
||||||
<Blog v-else-if="isBlogLayout" />
|
<template #page-bottom>
|
||||||
<Page v-else />
|
<slot name="page-bottom" />
|
||||||
</TransitionFadeSlideY>
|
</template>
|
||||||
</template>
|
<template #doc-footer-before>
|
||||||
<BackToTop />
|
<slot name="doc-footer-before" />
|
||||||
<VFooter />
|
</template>
|
||||||
</LayoutContent>
|
<template #doc-before>
|
||||||
|
<slot name="doc-before" />
|
||||||
|
</template>
|
||||||
|
<template #doc-after>
|
||||||
|
<slot name="doc-after" />
|
||||||
|
</template>
|
||||||
|
<template #doc-top>
|
||||||
|
<slot name="doc-top" />
|
||||||
|
</template>
|
||||||
|
<template #doc-bottom>
|
||||||
|
<slot name="doc-bottom" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #aside-top>
|
||||||
|
<slot name="aside-top" />
|
||||||
|
</template>
|
||||||
|
<template #aside-bottom>
|
||||||
|
<slot name="aside-bottom" />
|
||||||
|
</template>
|
||||||
|
<template #aside-outline-before>
|
||||||
|
<slot name="aside-outline-before" />
|
||||||
|
</template>
|
||||||
|
<template #aside-outline-after>
|
||||||
|
<slot name="aside-outline-after" />
|
||||||
|
</template>
|
||||||
|
</VPContent>
|
||||||
|
</slot>
|
||||||
|
<VPBackToTop />
|
||||||
|
<VPFooter />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<Content v-else />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.theme-plume {
|
.vp-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useRouteLocale, withBase } from 'vuepress/client'
|
import { useRouteLocale, withBase } from 'vuepress/client'
|
||||||
import LayoutContent from '../components/LayoutContent.vue'
|
|
||||||
import Nav from '../components/Nav/index.vue'
|
import Nav from '../components/Nav/index.vue'
|
||||||
|
import VPSkipLink from '../components/VPSkipLink.vue'
|
||||||
|
import VPFooter from '../components/VPFooter.vue'
|
||||||
import { useData } from '../composables/data.js'
|
import { useData } from '../composables/data.js'
|
||||||
|
|
||||||
const root = useRouteLocale()
|
const root = useRouteLocale()
|
||||||
@ -9,45 +10,68 @@ const { theme } = useData()
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="theme-plume">
|
<div vp-not-found class="theme-plume vp-layout">
|
||||||
<Nav />
|
<slot name="layout-top" />
|
||||||
<LayoutContent is-not-found>
|
<VPSkipLink />
|
||||||
<div class="not-found">
|
|
||||||
<p class="code">
|
|
||||||
{{ theme.notFound?.code ?? '404' }}
|
|
||||||
</p>
|
|
||||||
<h1 class="title">
|
|
||||||
{{ theme.notFound?.title ?? 'PAGE NOT FOUND' }}
|
|
||||||
</h1>
|
|
||||||
<div class="divider" />
|
|
||||||
<blockquote class="quote">
|
|
||||||
{{ theme.notFound?.quote ?? `But if you don't change your direction, and if you keep looking, you may end up where you are heading.` }}
|
|
||||||
</blockquote>
|
|
||||||
|
|
||||||
<div class="action">
|
<Nav />
|
||||||
<a class="link" :href="withBase(root)" :aria-label="theme.notFound?.linkLabel ?? 'go to home'">
|
<div id="VPContent" class="vp-content">
|
||||||
{{ theme.notFound?.linkText ?? 'Take me home' }}
|
<slot name="not-found">
|
||||||
</a>
|
<div class="vp-not-found">
|
||||||
|
<p class="code">
|
||||||
|
{{ theme.notFound?.code ?? '404' }}
|
||||||
|
</p>
|
||||||
|
<h1 class="title">
|
||||||
|
{{ theme.notFound?.title ?? 'PAGE NOT FOUND' }}
|
||||||
|
</h1>
|
||||||
|
<div class="divider" />
|
||||||
|
<blockquote class="quote">
|
||||||
|
{{
|
||||||
|
theme.notFound?.quote
|
||||||
|
?? "But if you don't change your direction, and if you keep looking, you may end up where you are heading."
|
||||||
|
}}
|
||||||
|
</blockquote>
|
||||||
|
|
||||||
|
<div class="action">
|
||||||
|
<a class="link" :href="withBase(root)" :aria-label="theme.notFound?.linkLabel ?? 'go to home'">
|
||||||
|
{{ theme.notFound?.linkText ?? 'Take me home' }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</slot>
|
||||||
</LayoutContent>
|
</div>
|
||||||
|
<VPFooter />
|
||||||
|
<slot name="layout-bottom" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.theme-plume {
|
.vp-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.not-found {
|
.vp-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin: var(--vp-layout-top-height, 0) auto 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 960px) {
|
||||||
|
.vp-content {
|
||||||
|
padding-top: var(--vp-nav-height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vp-not-found {
|
||||||
padding: 64px 24px 96px;
|
padding: 64px 24px 96px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.not-found {
|
.vp-not-found {
|
||||||
padding: 96px 32px 168px;
|
padding: 96px 32px 168px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +95,7 @@ const { theme } = useData()
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 24px auto 18px;
|
margin: 24px auto 18px;
|
||||||
background-color: var(--vp-c-divider);
|
background-color: var(--vp-c-divider);
|
||||||
|
transition: background-color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.quote {
|
.quote {
|
||||||
@ -79,6 +104,7 @@ const { theme } = useData()
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--vp-c-text-2);
|
color: var(--vp-c-text-2);
|
||||||
|
transition: color var(--t-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.action {
|
.action {
|
||||||
@ -90,16 +116,14 @@ const { theme } = useData()
|
|||||||
padding: 3px 16px;
|
padding: 3px 16px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--vp-c-brand);
|
color: var(--vp-c-brand-1);
|
||||||
border: 1px solid var(--vp-c-brand);
|
border: 1px solid var(--vp-c-brand-1);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
transition:
|
transition: color var(--t-color), border-color var(--t-color);
|
||||||
border-color 0.25s,
|
|
||||||
color 0.25s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.link:hover {
|
.link:hover {
|
||||||
color: var(--vp-c-brand-dark);
|
color: var(--vp-c-brand-2);
|
||||||
border-color: var(--vp-c-brand-dark);
|
border-color: var(--vp-c-brand-2);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* ------------------ Plugin Nprogress ------------------ */
|
||||||
#nprogress .bar {
|
#nprogress .bar {
|
||||||
background: var(--vp-c-brand-1);
|
background: var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
@ -10,3 +11,8 @@
|
|||||||
#nprogress .peg {
|
#nprogress .peg {
|
||||||
box-shadow: 0 0 10px var(--vp-c-brand-1), 0 0 5px var(--vp-c-brand-1);
|
box-shadow: 0 0 10px var(--vp-c-brand-1), 0 0 5px var(--vp-c-brand-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------ Plugin Comment ------------------ */
|
||||||
|
#vp-comment {
|
||||||
|
margin-top: 80px;
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
@import url("./normalize.css");
|
@import url("./normalize.css");
|
||||||
@import url("./icons.css");
|
@import url("./icons.css");
|
||||||
@import url("./social-icons.css");
|
@import url("./social-icons.css");
|
||||||
@import url("./nprogress.css");
|
@import url("./compat.css");
|
||||||
@import url("./utils.css");
|
@import url("./utils.css");
|
||||||
@import url("./content.css");
|
@import url("./content.css");
|
||||||
@import url("./code.css");
|
@import url("./code.css");
|
||||||
|
|||||||
@ -11,6 +11,7 @@ const FALLBACK_OPTIONS: PlumeThemeLocaleData = {
|
|||||||
article: '/article/',
|
article: '/article/',
|
||||||
notes: { link: '/', dir: '/notes/', notes: [] },
|
notes: { link: '/', dir: '/notes/', notes: [] },
|
||||||
navbarSocialInclude: ['github', 'twitter', 'discord', 'facebook'],
|
navbarSocialInclude: ['github', 'twitter', 'discord', 'facebook'],
|
||||||
|
aside: true,
|
||||||
outline: [2, 3],
|
outline: [2, 3],
|
||||||
|
|
||||||
// page meta
|
// page meta
|
||||||
|
|||||||
@ -38,19 +38,19 @@ export async function setupPage(
|
|||||||
// 添加 博客页面
|
// 添加 博客页面
|
||||||
pageList.push(createPage(app, {
|
pageList.push(createPage(app, {
|
||||||
path: withBase(link, localePath),
|
path: withBase(link, localePath),
|
||||||
frontmatter: { lang, type: 'blog', title: getTitle(locale, 'blog') },
|
frontmatter: { lang, _pageLayout: 'blog', title: getTitle(locale, 'blog') },
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 添加 标签页
|
// 添加 标签页
|
||||||
blog.tags !== false && pageList.push(createPage(app, {
|
blog.tags !== false && pageList.push(createPage(app, {
|
||||||
path: withBase(blog.tagsLink || `${link}/tags/`, localePath),
|
path: withBase(blog.tagsLink || `${link}/tags/`, localePath),
|
||||||
frontmatter: { lang, type: 'blog-tags', title: getTitle(locale, 'tag') },
|
frontmatter: { lang, _pageLayout: 'blog-tags', title: getTitle(locale, 'tag') },
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 添加归档页
|
// 添加归档页
|
||||||
blog.archives !== false && pageList.push(createPage(app, {
|
blog.archives !== false && pageList.push(createPage(app, {
|
||||||
path: withBase(blog.archivesLink || `${link}/archives/`, localePath),
|
path: withBase(blog.archivesLink || `${link}/archives/`, localePath),
|
||||||
frontmatter: { lang, type: 'blog-archives', title: getTitle(locale, 'archive') },
|
frontmatter: { lang, _pageLayout: 'blog-archives', title: getTitle(locale, 'archive') },
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
app.pages.push(...await Promise.all(pageList))
|
app.pages.push(...await Promise.all(pageList))
|
||||||
@ -61,22 +61,31 @@ export function extendsPageData(
|
|||||||
localeOptions: PlumeThemeLocaleOptions,
|
localeOptions: PlumeThemeLocaleOptions,
|
||||||
) {
|
) {
|
||||||
page.data.filePathRelative = page.filePathRelative
|
page.data.filePathRelative = page.filePathRelative
|
||||||
page.routeMeta.title = page.title
|
page.routeMeta.title = page.frontmatter.title || page.title
|
||||||
|
|
||||||
if (page.frontmatter.icon)
|
if (page.frontmatter.home) {
|
||||||
page.routeMeta.icon = page.frontmatter.icon
|
page.frontmatter.pageLayout = 'home'
|
||||||
|
delete page.frontmatter.home
|
||||||
|
}
|
||||||
|
|
||||||
if (page.frontmatter.friends) {
|
if (page.frontmatter.friends) {
|
||||||
page.frontmatter.article = false
|
page.frontmatter.article = false
|
||||||
page.frontmatter.type = 'friends'
|
page.data.type = 'friends'
|
||||||
page.data.isBlogPost = false
|
|
||||||
page.permalink = page.permalink ?? '/friends/'
|
page.permalink = page.permalink ?? '/friends/'
|
||||||
|
page.frontmatter.pageLayout = 'friends'
|
||||||
|
delete page.frontmatter.friends
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((page.frontmatter.type as string)?.startsWith('blog')) {
|
const pageType = page.frontmatter._pageLayout as string
|
||||||
page.data.isBlogPost = false
|
if (pageType) {
|
||||||
page.frontmatter.article = false
|
page.frontmatter.article = false
|
||||||
page.data.type = page.frontmatter.type as any
|
page.data.type = pageType as any
|
||||||
|
delete page.frontmatter._pageLayout
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('externalLink' in page.frontmatter) {
|
||||||
|
page.frontmatter.externalLinkIcon = page.frontmatter.externalLink
|
||||||
|
delete page.frontmatter.externalLink
|
||||||
}
|
}
|
||||||
|
|
||||||
autoCategory(page, localeOptions)
|
autoCategory(page, localeOptions)
|
||||||
@ -86,6 +95,7 @@ export function extendsPageData(
|
|||||||
let uuid = 10000
|
let uuid = 10000
|
||||||
const cache: Record<string, number> = {}
|
const cache: Record<string, number> = {}
|
||||||
const RE_CATEGORY = /^(\d+)?(?:\.?)([^]+)$/
|
const RE_CATEGORY = /^(\d+)?(?:\.?)([^]+)$/
|
||||||
|
let LOCALE_RE: RegExp
|
||||||
|
|
||||||
export function autoCategory(
|
export function autoCategory(
|
||||||
page: Page<PlumeThemePageData>,
|
page: Page<PlumeThemePageData>,
|
||||||
@ -93,18 +103,18 @@ export function autoCategory(
|
|||||||
) {
|
) {
|
||||||
const pagePath = page.filePathRelative
|
const pagePath = page.filePathRelative
|
||||||
|
|
||||||
if (page.frontmatter.type || !pagePath)
|
if (page.data.type || !pagePath)
|
||||||
return
|
return
|
||||||
const notesLinks = resolveNotesLinkList(options)
|
const notesLinks = resolveNotesLinkList(options)
|
||||||
|
|
||||||
if (notesLinks.some(link => page.path.startsWith(link)))
|
if (notesLinks.some(link => page.path.startsWith(link)))
|
||||||
return
|
return
|
||||||
|
|
||||||
const RE_LOCALE = new RegExp(
|
LOCALE_RE ??= new RegExp(
|
||||||
`^(${Object.keys(options.locales || {}).filter(l => l !== '/').join('|')})`,
|
`^(${Object.keys(options.locales || {}).filter(l => l !== '/').join('|')})`,
|
||||||
)
|
)
|
||||||
const categoryList: PageCategoryData[] = ensureLeadingSlash(pagePath)
|
const categoryList: PageCategoryData[] = ensureLeadingSlash(pagePath)
|
||||||
.replace(RE_LOCALE, '')
|
.replace(LOCALE_RE, '')
|
||||||
.replace(/^\//, '')
|
.replace(/^\//, '')
|
||||||
.split('/')
|
.split('/')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import type { ThemeImage } from '../base.js'
|
import type { ThemeImage } from '../base.js'
|
||||||
|
import type { PlumeNormalFrontmatter } from './normal.js'
|
||||||
|
|
||||||
export interface PlumeThemeHomeFrontmatter extends Omit<PlumeThemeHomeBanner, 'type'> {
|
export interface PlumeThemeHomeFrontmatter extends PlumeNormalFrontmatter, Omit<PlumeThemeHomeBanner, 'type'> {
|
||||||
home?: true
|
home?: true
|
||||||
|
friends?: never
|
||||||
config?: PlumeThemeHomeConfig[]
|
config?: PlumeThemeHomeConfig[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
theme/src/shared/frontmatter/normal.ts
Normal file
43
theme/src/shared/frontmatter/normal.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import type { PageFrontmatter } from 'vuepress'
|
||||||
|
|
||||||
|
export interface PlumeNormalFrontmatter extends PageFrontmatter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* 使用 pageLayout = 'home' 代替
|
||||||
|
*/
|
||||||
|
home?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*
|
||||||
|
* 使用 pageLayout = 'friends' 代替
|
||||||
|
*/
|
||||||
|
friends?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* page layout
|
||||||
|
*/
|
||||||
|
pageLayout?: false | 'home' | 'doc' | 'custom' | 'page' | 'friends'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义页面 class
|
||||||
|
*/
|
||||||
|
pageClass?: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示返回顶部按钮
|
||||||
|
*/
|
||||||
|
backToTop?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前页面是否显示 外部链接图标
|
||||||
|
*/
|
||||||
|
externalLinkIcon?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 使用 `externalLinkIcon` 代替
|
||||||
|
*/
|
||||||
|
externalLink?: boolean
|
||||||
|
}
|
||||||
@ -1,21 +1,65 @@
|
|||||||
import type { WatermarkPluginFrontmatter } from '@vuepress/plugin-watermark'
|
import type { WatermarkPluginFrontmatter } from '@vuepress/plugin-watermark'
|
||||||
import type { ThemeOutline } from '../base.js'
|
import type { ThemeOutline } from '../base.js'
|
||||||
import type { NavItemWithLink } from '../navbar.js'
|
import type { NavItemWithLink } from '../navbar.js'
|
||||||
|
import type { PlumeNormalFrontmatter } from './normal.js'
|
||||||
|
|
||||||
export interface PlumeThemePageFrontmatter {
|
export interface PlumeThemePageFrontmatter extends PlumeNormalFrontmatter {
|
||||||
home?: false
|
home?: never
|
||||||
|
friends?: never
|
||||||
|
/**
|
||||||
|
* 是否开启评论
|
||||||
|
*/
|
||||||
comments?: boolean
|
comments?: boolean
|
||||||
|
/**
|
||||||
|
* 是否显示编辑按钮
|
||||||
|
*/
|
||||||
editLink?: boolean
|
editLink?: boolean
|
||||||
|
/**
|
||||||
|
* 编辑链接模式
|
||||||
|
*/
|
||||||
editLinkPattern?: string
|
editLinkPattern?: string
|
||||||
|
/**
|
||||||
|
* 是否显示最近更新时间
|
||||||
|
*/
|
||||||
lastUpdated?: boolean
|
lastUpdated?: boolean
|
||||||
|
/**
|
||||||
|
* 是否显示贡献者
|
||||||
|
*/
|
||||||
contributors?: boolean
|
contributors?: boolean
|
||||||
|
/**
|
||||||
|
* 上一篇
|
||||||
|
*/
|
||||||
prev?: string | NavItemWithLink
|
prev?: string | NavItemWithLink
|
||||||
|
/**
|
||||||
|
* 下一篇
|
||||||
|
*/
|
||||||
next?: string | NavItemWithLink
|
next?: string | NavItemWithLink
|
||||||
|
/**
|
||||||
|
* 是否显示侧边栏,也可以强制指定当前页面显示哪个侧边栏
|
||||||
|
*/
|
||||||
sidebar?: string | false
|
sidebar?: string | false
|
||||||
aside?: boolean
|
/**
|
||||||
|
* 是否显示页内侧边栏
|
||||||
|
*/
|
||||||
|
aside?: boolean | 'left'
|
||||||
|
/**
|
||||||
|
* 是否显示内容大纲,仅在页内侧边栏开启时生效
|
||||||
|
*/
|
||||||
outline?: ThemeOutline
|
outline?: ThemeOutline
|
||||||
backToTop?: boolean
|
/**
|
||||||
externalLink?: boolean
|
* 是否显示阅读时间、字数
|
||||||
|
*/
|
||||||
readingTime?: boolean
|
readingTime?: boolean
|
||||||
|
/**
|
||||||
|
* 水印配置
|
||||||
|
*/
|
||||||
watermark?: WatermarkPluginFrontmatter['watermark'] & { fullPage?: boolean }
|
watermark?: WatermarkPluginFrontmatter['watermark'] & { fullPage?: boolean }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用作 navbar 、 sidebar 的图标
|
||||||
|
* 支持 iconify 图标,直接使用 iconify name 即可自动加载
|
||||||
|
* 支持 本地、远程 svg 图标,直接使用 svg 的 url 即可
|
||||||
|
* 或直接传入 svg 字符串
|
||||||
|
*/
|
||||||
|
icon?: string | { svg: string }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,6 +76,13 @@ export interface PlumeThemeLocaleData extends LocaleData {
|
|||||||
|
|
||||||
outline?: ThemeOutline
|
outline?: ThemeOutline
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示侧边栏
|
||||||
|
*
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
aside?: boolean | 'left'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* language text
|
* language text
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export interface PlumeThemePluginOptions {
|
|||||||
/**
|
/**
|
||||||
* git 插件 配置
|
* git 插件 配置
|
||||||
*/
|
*/
|
||||||
git?: false
|
git?: boolean
|
||||||
|
|
||||||
nprogress?: false
|
nprogress?: false
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user