commit
b9aacd68e9
33
.github/workflows/lint.yml
vendored
Normal file
33
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
name: Linter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: pnpm
|
||||
|
||||
- name: Install deps
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Linter
|
||||
run: |
|
||||
pnpm run lint
|
||||
pnpm run lint:css
|
||||
23
.github/workflows/release.yml
vendored
Normal file
23
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: Add Release Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: github.repository == 'pengzhanbo/vuepress-theme-plume'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- run: npx changelogithub
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
@ -20,6 +20,12 @@ __options__ : `PlumeThemeOptions`
|
||||
|
||||
查看 [主题配置](/config/basic/) 了解更多。
|
||||
|
||||
## `defineThemeConfig(options)`
|
||||
|
||||
主题配置帮助函数,用于在单独的 `plume.config.ts` 中使用。
|
||||
|
||||
查看 [主题配置文件](../config/配置说明.md#主题配置文件) 了解更多。
|
||||
|
||||
## `defineNavbarConfig(options)`
|
||||
|
||||
主题导航栏配置函数。
|
||||
|
||||
@ -8,12 +8,12 @@ permalink: /guide/layout-slots/
|
||||
|
||||
## 概述
|
||||
|
||||
主题通过 `<Layout />` 提供了 丰富的 布局插槽,可以通过这些插槽,在 页面 的不同位置注入内容。
|
||||
主题通过 `<Layout />` 和 `<NotFound />` 提供了 丰富的 布局插槽,可以通过这些插槽,在 页面 的不同位置注入内容。
|
||||
以便用户可以个性化的使用主题。
|
||||
|
||||
## 使用
|
||||
|
||||
首先,需要创建一个 客户端配置文件: `.vuepress/client.ts`:
|
||||
以 `<Layout />` 为例,首先,需要创建一个 客户端配置文件: `.vuepress/client.ts`:
|
||||
|
||||
```ts
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
@ -69,7 +69,7 @@ export default defineClientConfig({
|
||||
|
||||
## 插槽
|
||||
|
||||
主题支持以下插槽:
|
||||
### `<Layout />` 插槽
|
||||
|
||||
- 当 `pageLayout: doc` 时:
|
||||
|
||||
@ -115,13 +115,24 @@ export default defineClientConfig({
|
||||
- `blog-archives-before`
|
||||
- `blog-archives-after`
|
||||
|
||||
- 总是启用:
|
||||
- 在 博客分类页 中:
|
||||
|
||||
- `layout-top`
|
||||
- `layout-bottom`
|
||||
- `nav-bar-title-before`
|
||||
- `nav-bar-title-after`
|
||||
- `nav-bar-content-before`
|
||||
- `nav-bar-content-after`
|
||||
- `nav-screen-content-before`
|
||||
- `nav-screen-content-after`
|
||||
- `blog-categories-before`
|
||||
- `blog-categories-after`
|
||||
|
||||
### `<NotFound />` 插槽
|
||||
|
||||
- `not-found`
|
||||
|
||||
### 通用插槽
|
||||
|
||||
以下插槽在 `<Layout />` 和 `<NotFound />` 中都支持:
|
||||
|
||||
- `layout-top`
|
||||
- `layout-bottom`
|
||||
- `nav-bar-title-before`
|
||||
- `nav-bar-title-after`
|
||||
- `nav-bar-content-before`
|
||||
- `nav-bar-content-after`
|
||||
- `nav-screen-content-before`
|
||||
- `nav-screen-content-after`
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"vuepress": "2.0.0-rc.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/json": "^2.2.225",
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@vuepress/bundler-vite": "2.0.0-rc.14",
|
||||
"chart.js": "^4.4.3",
|
||||
"echarts": "^5.5.1",
|
||||
|
||||
10
package.json
10
package.json
@ -41,8 +41,8 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@pengzhanbo/eslint-config-vue": "^1.11.2",
|
||||
"@pengzhanbo/stylelint-config": "^1.11.2",
|
||||
"@pengzhanbo/eslint-config-vue": "^1.12.0",
|
||||
"@pengzhanbo/stylelint-config": "^1.12.0",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@types/node": "20.12.10",
|
||||
"@types/webpack-env": "^1.18.5",
|
||||
@ -52,11 +52,11 @@
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"cpx2": "^7.0.1",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^9.6.0",
|
||||
"eslint": "^9.7.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.7",
|
||||
"rimraf": "^5.0.8",
|
||||
"stylelint": "^16.6.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"stylelint": "^16.7.0",
|
||||
"tsconfig-vuepress": "^4.5.0",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
|
||||
@ -47,18 +47,18 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify/utils": "^2.1.25",
|
||||
"@vuepress/helper": "2.0.0-rc.37",
|
||||
"@vuepress/helper": "2.0.0-rc.38",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"local-pkg": "^0.5.0",
|
||||
"markdown-it-container": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"shiki": "^1.10.3",
|
||||
"tm-grammars": "^1.13.7",
|
||||
"tm-themes": "^1.5.1",
|
||||
"tm-grammars": "^1.13.11",
|
||||
"tm-themes": "^1.5.3",
|
||||
"vue": "^3.4.31"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.225",
|
||||
"@iconify/json": "^2.2.228",
|
||||
"@types/markdown-it": "^14.1.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@ -48,7 +48,9 @@ function highlight() {
|
||||
}
|
||||
|
||||
function updateScroll() {
|
||||
container && (container.scrollLeft = textAreaEl.value?.scrollLeft || 0)
|
||||
if (container) {
|
||||
container.scrollLeft = textAreaEl.value?.scrollLeft || 0
|
||||
}
|
||||
}
|
||||
|
||||
watch([input], highlight, { flush: 'post' })
|
||||
|
||||
@ -11,12 +11,19 @@ const SANDBOX = 'allow-forms allow-modals allow-popups allow-presentation allow-
|
||||
|
||||
const source = computed(() => {
|
||||
const params = new URLSearchParams()
|
||||
props.filepath && params.set(props.type === 'embed' ? 'module' : 'file', encodeURIComponent(props.filepath))
|
||||
if (props.filepath) {
|
||||
params.set(props.type === 'embed' ? 'module' : 'file', encodeURIComponent(props.filepath))
|
||||
}
|
||||
|
||||
if (props.type === 'embed') {
|
||||
params.set('view', props.layout ? props.layout.replace(/,/g, '+') : 'Editor+Preview')
|
||||
props.console && params.set('expanddevtools', '1')
|
||||
props.navbar === false && params.set('hidenavigation', '1')
|
||||
|
||||
if (props.console) {
|
||||
params.set('expanddevtools', '1')
|
||||
}
|
||||
if (props.navbar === false) {
|
||||
params.set('hidenavigation', '1')
|
||||
}
|
||||
}
|
||||
else {
|
||||
params.set('from-embed', '')
|
||||
|
||||
@ -34,9 +34,16 @@ export const codepenPlugin: PluginWithOptions<never> = (md) => {
|
||||
content: (meta) => {
|
||||
const { title = 'Codepen', height, width } = meta
|
||||
const params = new URLSearchParams()
|
||||
meta.editable && params.set('editable', 'true')
|
||||
meta.tab && params.set('default-tab', meta.tab)
|
||||
meta.theme && params.set('theme-id', meta.theme)
|
||||
|
||||
if (meta.editable) {
|
||||
params.set('editable', 'true')
|
||||
}
|
||||
if (meta.tab) {
|
||||
params.set('default-tab', meta.tab)
|
||||
}
|
||||
if (meta.theme) {
|
||||
params.set('theme-id', meta.theme)
|
||||
}
|
||||
|
||||
const middle = meta.preview ? '/embed/preview/' : '/embed/'
|
||||
|
||||
|
||||
@ -130,7 +130,7 @@ async function genIconContent(iconName: string, cb: (content: string) => void) {
|
||||
iconJson = JSON.parse(await fs.readFile(filename, 'utf-8'))
|
||||
iconDataCache.set(collect, iconJson)
|
||||
}
|
||||
catch (e) {
|
||||
catch {
|
||||
logger.warn(`[plugin-md-power] Can not find icon, ${collect} is missing!`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,10 +33,15 @@ export async function langReplPlugin(app: App, md: markdownIt, {
|
||||
kotlin = false,
|
||||
rust = false,
|
||||
}: ReplOptions) {
|
||||
kotlin && createReplContainer(md, 'kotlin')
|
||||
go && createReplContainer(md, 'go')
|
||||
rust && createReplContainer(md, 'rust')
|
||||
|
||||
if (kotlin) {
|
||||
createReplContainer(md, 'kotlin')
|
||||
}
|
||||
if (go) {
|
||||
createReplContainer(md, 'go')
|
||||
}
|
||||
if (rust) {
|
||||
createReplContainer(md, 'rust')
|
||||
}
|
||||
theme ??= { light: 'github-light', dark: 'github-dark' }
|
||||
|
||||
const data: ReplEditorData = { grammars: {} } as ReplEditorData
|
||||
|
||||
@ -41,11 +41,26 @@ export const bilibiliPlugin: PluginWithOptions<never> = (md) => {
|
||||
content(meta) {
|
||||
const params = new URLSearchParams()
|
||||
|
||||
meta.bvid && params.set('bvid', meta.bvid)
|
||||
meta.aid && params.set('aid', meta.aid)
|
||||
meta.cid && params.set('cid', meta.cid)
|
||||
meta.page && params.set('p', meta.page.toString())
|
||||
meta.time && params.set('t', meta.time.toString())
|
||||
if (meta.bvid) {
|
||||
params.set('bvid', meta.bvid)
|
||||
}
|
||||
|
||||
if (meta.aid) {
|
||||
params.set('aid', meta.aid)
|
||||
}
|
||||
|
||||
if (meta.cid) {
|
||||
params.set('cid', meta.cid)
|
||||
}
|
||||
|
||||
if (meta.page) {
|
||||
params.set('p', meta.page.toString())
|
||||
}
|
||||
|
||||
if (meta.time) {
|
||||
params.set('t', meta.time.toString())
|
||||
}
|
||||
|
||||
params.set('autoplay', meta.autoplay ? '1' : '0')
|
||||
|
||||
const source = `${BILIBILI_LINK}?${params.toString()}`
|
||||
|
||||
@ -34,10 +34,21 @@ export const youtubePlugin: PluginWithOptions<never> = (md) => {
|
||||
content(meta) {
|
||||
const params = new URLSearchParams()
|
||||
|
||||
meta.autoplay && params.set('autoplay', '1')
|
||||
meta.loop && params.set('loop', '1')
|
||||
meta.start && params.set('start', meta.start.toString())
|
||||
meta.end && params.set('end', meta.end.toString())
|
||||
if (meta.autoplay) {
|
||||
params.set('autoplay', '1')
|
||||
}
|
||||
|
||||
if (meta.loop) {
|
||||
params.set('loop', '1')
|
||||
}
|
||||
|
||||
if (meta.start) {
|
||||
params.set('start', meta.start.toString())
|
||||
}
|
||||
|
||||
if (meta.end) {
|
||||
params.set('end', meta.end.toString())
|
||||
}
|
||||
|
||||
const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}`
|
||||
|
||||
|
||||
@ -32,11 +32,13 @@ export function markdownPowerPlugin(options: MarkdownPowerPluginOptions = {}): P
|
||||
onInitialized: async () => await initIcon(),
|
||||
|
||||
extendsBundlerOptions(bundlerOptions) {
|
||||
options.repl && addViteOptimizeDepsInclude(
|
||||
bundlerOptions,
|
||||
app,
|
||||
['shiki/core', 'shiki/wasm'],
|
||||
)
|
||||
if (options.repl) {
|
||||
addViteOptimizeDepsInclude(
|
||||
bundlerOptions,
|
||||
app,
|
||||
['shiki/core', 'shiki/wasm'],
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
extendsMarkdown: async (md: MarkdownIt, app) => {
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
"vuepress": "2.0.0-rc.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vuepress/helper": "2.0.0-rc.37",
|
||||
"@vuepress/helper": "2.0.0-rc.38",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"@vueuse/integrations": "^10.11.0",
|
||||
"chokidar": "^3.6.0",
|
||||
|
||||
@ -163,7 +163,9 @@ const disableReset = computed(() => {
|
||||
})
|
||||
function focusSearchInput(select = true) {
|
||||
searchInput.value?.focus()
|
||||
select && searchInput.value?.select()
|
||||
if (select) {
|
||||
searchInput.value?.select()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
"@shikijs/transformers": "^1.10.3",
|
||||
"@shikijs/twoslash": "^1.10.3",
|
||||
"@types/hast": "^3.0.4",
|
||||
"@vuepress/helper": "2.0.0-rc.37",
|
||||
"@vuepress/helper": "2.0.0-rc.38",
|
||||
"floating-vue": "^5.2.2",
|
||||
"mdast-util-from-markdown": "^2.0.1",
|
||||
"mdast-util-gfm": "^3.0.0",
|
||||
|
||||
1122
pnpm-lock.yaml
generated
1122
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -74,18 +74,18 @@
|
||||
"@vuepress-plume/plugin-iconify": "workspace:*",
|
||||
"@vuepress-plume/plugin-search": "workspace:*",
|
||||
"@vuepress-plume/plugin-shikiji": "workspace:*",
|
||||
"@vuepress/helper": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-active-header-links": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-comment": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-docsearch": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-git": "2.0.0-rc.37",
|
||||
"@vuepress/helper": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-active-header-links": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-comment": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-docsearch": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-git": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-markdown-container": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-nprogress": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-photo-swipe": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-reading-time": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-seo": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-sitemap": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-watermark": "2.0.0-rc.37",
|
||||
"@vuepress/plugin-nprogress": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-photo-swipe": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-reading-time": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-seo": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-sitemap": "2.0.0-rc.38",
|
||||
"@vuepress/plugin-watermark": "2.0.0-rc.38",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"bcrypt-ts": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
@ -99,7 +99,7 @@
|
||||
"nanoid": "^5.0.7",
|
||||
"vue": "^3.4.31",
|
||||
"vue-router": "^4.4.0",
|
||||
"vuepress-plugin-md-enhance": "2.0.0-rc.50",
|
||||
"vuepress-plugin-md-enhance": "2.0.0-rc.51",
|
||||
"vuepress-plugin-md-power": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ const { theme, page } = useData()
|
||||
<slot name="blog-categories-before" />
|
||||
</template>
|
||||
<template #blog-categories-after>
|
||||
<slot name="blog-tags-after" />
|
||||
<slot name="blog-categories-after" />
|
||||
</template>
|
||||
</VPBlogCategories>
|
||||
<VPPostList v-else>
|
||||
|
||||
@ -12,7 +12,7 @@ import VPNavBarTranslations from '@theme/Nav/VPNavBarTranslations.vue'
|
||||
import { useData } from '../../composables/data.js'
|
||||
import { useSidebar } from '../../composables/sidebar.js'
|
||||
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
isScreenOpen: boolean
|
||||
}>()
|
||||
defineEmits<(e: 'toggleScreen') => void>()
|
||||
@ -28,6 +28,7 @@ watchPostEffect(() => {
|
||||
'has-sidebar': hasSidebar.value,
|
||||
'home': frontmatter.value.pageLayout === 'home',
|
||||
'top': y.value === 0,
|
||||
'screen-open': props.isScreenOpen,
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -83,6 +84,12 @@ watchPostEffect(() => {
|
||||
transition-property: background-color, color, border-bottom;
|
||||
}
|
||||
|
||||
.vp-navbar.screen-open {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
border-bottom: 1px solid var(--vp-c-divider);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.vp-navbar:not(.home) {
|
||||
background-color: var(--vp-nav-bg-color);
|
||||
}
|
||||
@ -240,6 +247,11 @@ watchPostEffect(() => {
|
||||
margin-right: -8px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.vp-navbar.has-sidebar .divider {
|
||||
padding-left: var(--vp-sidebar-width);
|
||||
@ -252,6 +264,10 @@ watchPostEffect(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.vp-navbar.screen-open .divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.divider-line {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
@ -48,8 +48,7 @@ const isLocked = useScrollLock(inBrowser ? document.body : null)
|
||||
overflow-y: auto;
|
||||
pointer-events: auto;
|
||||
background-color: var(--vp-nav-screen-bg-color);
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
transition: background-color var(--t-color), border-top var(--t-color);
|
||||
transition: background-color var(--t-color);
|
||||
}
|
||||
|
||||
.container {
|
||||
|
||||
@ -40,7 +40,9 @@ const show = computed(() => {
|
||||
|
||||
let timer: NodeJS.Timeout | null = null
|
||||
function resetScrolling() {
|
||||
timer && clearTimeout(timer)
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
isScrolling.value = false
|
||||
}, 1000)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, watch } from 'vue'
|
||||
import { nextTick, watch } from 'vue'
|
||||
import VPBlog from '@theme/Blog/VPBlog.vue'
|
||||
import VPDoc from '@theme/VPDoc.vue'
|
||||
import VPPage from '@theme/VPPage.vue'
|
||||
@ -7,18 +7,15 @@ import VPHome from '@theme/Home/VPHome.vue'
|
||||
import VPFriends from '@theme/VPFriends.vue'
|
||||
import { useData, useSidebar } from '../composables/index.js'
|
||||
import { inBrowser } from '../utils/index.js'
|
||||
import { useBlogPageData } from '../composables/page.js'
|
||||
|
||||
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' || type === 'blog-categories'
|
||||
})
|
||||
const { frontmatter } = useData()
|
||||
const { isBlogLayout } = useBlogPageData()
|
||||
|
||||
watch([isBlogLayout, () => frontmatter.value.pageLayout], () => nextTick(() =>
|
||||
inBrowser && document.documentElement.classList.toggle(
|
||||
@ -54,6 +51,12 @@ watch([isBlogLayout, () => frontmatter.value.pageLayout], () => nextTick(() =>
|
||||
<template #blog-tags-after>
|
||||
<slot name="blog-tags-after" />
|
||||
</template>
|
||||
<template #blog-categories-before>
|
||||
<slot name="blog-categories-before" />
|
||||
</template>
|
||||
<template #blog-categories-after>
|
||||
<slot name="blog-categories-after" />
|
||||
</template>
|
||||
<template #blog-post-list-before>
|
||||
<slot name="blog-post-list-before" />
|
||||
</template>
|
||||
|
||||
@ -10,13 +10,13 @@ import { useEncrypt } from '../composables/encrypt.js'
|
||||
import { useSidebar } from '../composables/sidebar.js'
|
||||
import { useData } from '../composables/data.js'
|
||||
import { useHeaders } from '../composables/outline.js'
|
||||
import { useBlogPost } from '../composables/page.js'
|
||||
import { useBlogPageData } from '../composables/page.js'
|
||||
|
||||
const { page, theme, frontmatter, isDark } = useData()
|
||||
const route = useRoute()
|
||||
|
||||
const { hasSidebar, hasAside, leftAside } = useSidebar()
|
||||
const { isBlogPost } = useBlogPost()
|
||||
const { isBlogPost } = useBlogPageData()
|
||||
const headers = useHeaders()
|
||||
const { isPageDecrypted } = useEncrypt()
|
||||
|
||||
|
||||
@ -4,11 +4,11 @@ import { useReadingTimeLocale } from '@vuepress/plugin-reading-time/client'
|
||||
import VPLink from '@theme/VPLink.vue'
|
||||
import { useData } from '../composables/data.js'
|
||||
import { useTagColors } from '../composables/tag-colors.js'
|
||||
import { useBlogPost } from '../composables/page.js'
|
||||
import { useBlogPageData } from '../composables/page.js'
|
||||
import { useBlogExtract } from '../composables/blog-extract.js'
|
||||
|
||||
const { page, frontmatter: matter } = useData<'post'>()
|
||||
const { isBlogPost } = useBlogPost()
|
||||
const { isBlogPost } = useBlogPageData()
|
||||
const colors = useTagColors()
|
||||
const readingTime = useReadingTimeLocale()
|
||||
const { categories } = useBlogExtract()
|
||||
@ -53,7 +53,7 @@ const hasMeta = computed(() => readingTime.value.time || tags.value.length || cr
|
||||
<span v-if="index !== categoryList.length - 1" class="dot">›</span>
|
||||
</template>
|
||||
</div>
|
||||
<h1 class="vp-doc-title" :class="{ padding: !hasMeta }">
|
||||
<h1 class="vp-doc-title page-title" :class="{ padding: !hasMeta }">
|
||||
{{ page.title }}
|
||||
</h1>
|
||||
<div v-if="hasMeta" class="vp-doc-meta">
|
||||
|
||||
@ -78,7 +78,7 @@ onMounted(() => {
|
||||
margin-left: var(--vp-sidebar-width);
|
||||
}
|
||||
|
||||
.vp-footer.vp-footer.has-sidebar .container {
|
||||
.vp-footer.has-sidebar .container {
|
||||
margin-left: calc(0px - var(--vp-sidebar-width));
|
||||
}
|
||||
}
|
||||
@ -87,11 +87,9 @@ onMounted(() => {
|
||||
.vp-footer {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.vp-footer.has-sidebar {
|
||||
margin-left: calc(
|
||||
(100% - (var(--vp-layout-max-width) - 64px)) / 2 + var(--vp-sidebar-width) -
|
||||
32px
|
||||
)
|
||||
margin-left: calc((100% - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import VPLocalNavOutlineDropdown from '@theme/VPLocalNavOutlineDropdown.vue'
|
||||
import { useSidebar } from '../composables/sidebar.js'
|
||||
import { useHeaders } from '../composables/outline.js'
|
||||
import { useData } from '../composables/data.js'
|
||||
import { useBlogPost } from '../composables/page.js'
|
||||
import { useBlogPageData } from '../composables/page.js'
|
||||
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
@ -15,7 +15,7 @@ const props = defineProps<{
|
||||
defineEmits<(e: 'openMenu') => void>()
|
||||
|
||||
const { theme } = useData()
|
||||
const { isBlogPost } = useBlogPost()
|
||||
const { isBlogPost } = useBlogPageData()
|
||||
|
||||
const { hasSidebar } = useSidebar()
|
||||
const { y } = useWindowScroll()
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { useScrollLock } from '@vueuse/core'
|
||||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useRoutePath } from 'vuepress/client'
|
||||
import VPSidebarItem from '@theme/VPSidebarItem.vue'
|
||||
import VPSidebarGroup from '@theme/VPSidebarGroup.vue'
|
||||
import VPTransitionFadeSlideY from '@theme/VPTransitionFadeSlideY.vue'
|
||||
import { useSidebar } from '../composables/sidebar.js'
|
||||
import { inBrowser } from '../utils/index.js'
|
||||
@ -71,13 +71,7 @@ onMounted(() => {
|
||||
|
||||
<slot name="sidebar-nav-before" />
|
||||
|
||||
<div
|
||||
v-for="item in sidebarGroups"
|
||||
:key="item.text"
|
||||
class="group"
|
||||
>
|
||||
<VPSidebarItem :item="item" :depth="0" />
|
||||
</div>
|
||||
<VPSidebarGroup :items="sidebarGroups" />
|
||||
|
||||
<slot name="sidebar-nav-after" />
|
||||
</nav>
|
||||
@ -170,17 +164,4 @@ onMounted(() => {
|
||||
.nav {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.group + .group {
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
transition: border-top var(--t-color);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.group {
|
||||
width: calc(var(--vp-sidebar-width) - 64px);
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
55
theme/src/client/components/VPSidebarGroup.vue
Normal file
55
theme/src/client/components/VPSidebarGroup.vue
Normal file
@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import type { ResolvedSidebarItem } from '../../shared/resolved/sidebar.js'
|
||||
import VPSidebarItem from './VPSidebarItem.vue'
|
||||
|
||||
defineProps<{
|
||||
items: ResolvedSidebarItem[]
|
||||
}>()
|
||||
|
||||
const disableTransition = ref(true)
|
||||
let timer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
onMounted(() => {
|
||||
timer = setTimeout(() => {
|
||||
timer = null
|
||||
disableTransition.value = false
|
||||
}, 300)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (timer != null) {
|
||||
clearTimeout(timer)
|
||||
timer = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.text"
|
||||
class="group"
|
||||
:class="{ 'no-transition': disableTransition }"
|
||||
>
|
||||
<VPSidebarItem :item="item" :depth="0" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.no-transition :deep(.caret-icon) {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.group + .group {
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid var(--vp-c-divider);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.group {
|
||||
width: calc(var(--vp-sidebar-width) - 64px);
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -47,16 +47,20 @@ function onItemInteraction(e: MouseEvent | Event) {
|
||||
if ('key' in e && e.key !== 'Enter')
|
||||
return
|
||||
|
||||
!props.item.link && toggle()
|
||||
if (!props.item.link) {
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
|
||||
function onCaretClick() {
|
||||
props.item.link && toggle()
|
||||
if (props.item.link) {
|
||||
toggle()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Component :is="sectionTag" class="vp-sidebar-item" :class="classes">
|
||||
<Component :is="sectionTag" class="vp-sidebar-item sidebar-item" :class="classes">
|
||||
<div
|
||||
v-if="item.text"
|
||||
class="item"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, ref } from 'vue'
|
||||
import { inject, ref, watchPostEffect } from 'vue'
|
||||
import VPSwitch from '@theme/VPSwitch.vue'
|
||||
import { useData } from '../composables/data.js'
|
||||
|
||||
@ -10,8 +10,9 @@ const toggleAppearance = inject('toggle-appearance', () => {
|
||||
isDark.value = !isDark.value
|
||||
})
|
||||
|
||||
const switchTitle = computed(() => {
|
||||
return isDark.value
|
||||
const switchTitle = ref('')
|
||||
watchPostEffect(() => {
|
||||
switchTitle.value = isDark.value
|
||||
? theme.value.lightModeSwitchTitle || 'Switch to light theme'
|
||||
: theme.value.darkModeSwitchTitle || 'Switch to dark theme'
|
||||
})
|
||||
|
||||
@ -17,7 +17,9 @@ export function useFlyout(options: UseFlyoutOptions) {
|
||||
const focus = ref(false)
|
||||
|
||||
if (inBrowser) {
|
||||
!active && activateFocusTracking()
|
||||
if (!active) {
|
||||
activateFocusTracking()
|
||||
}
|
||||
|
||||
listeners++
|
||||
|
||||
|
||||
@ -91,13 +91,17 @@ export function useHomeHeroTintPlate(
|
||||
onMounted(() => {
|
||||
if (canvas.value && enable.value) {
|
||||
ctx = canvas.value.getContext('2d')!
|
||||
timer && window.cancelAnimationFrame(timer)
|
||||
if (timer) {
|
||||
window.cancelAnimationFrame(timer)
|
||||
}
|
||||
run()
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
timer && window.cancelAnimationFrame(timer)
|
||||
if (timer) {
|
||||
window.cancelAnimationFrame(timer)
|
||||
}
|
||||
})
|
||||
|
||||
function run() {
|
||||
|
||||
@ -4,7 +4,7 @@ import { normalizeLink } from '../utils/index.js'
|
||||
import { useThemeData } from './theme-data.js'
|
||||
import { useData } from './data.js'
|
||||
import { getSidebarFirstLink, useSidebarData } from './sidebar.js'
|
||||
import { useBlogPost } from './page.js'
|
||||
import { useBlogPageData } from './page.js'
|
||||
|
||||
export function useLangs({
|
||||
removeCurrent = true,
|
||||
@ -13,7 +13,7 @@ export function useLangs({
|
||||
const { page } = useData()
|
||||
const routeLocale = useRouteLocale()
|
||||
const sidebar = useSidebarData()
|
||||
const { isBlogPost } = useBlogPost()
|
||||
const { isBlogPost } = useBlogPageData()
|
||||
|
||||
const currentLang = computed(() => {
|
||||
const link = routeLocale.value
|
||||
|
||||
@ -60,14 +60,21 @@ export function useNav(): UseNavReturn {
|
||||
}
|
||||
|
||||
function toggleScreen(): void {
|
||||
isScreenOpen.value ? closeScreen() : openScreen()
|
||||
if (isScreenOpen.value) {
|
||||
closeScreen()
|
||||
}
|
||||
else {
|
||||
openScreen()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close screen when the user resizes the window wider than tablet size.
|
||||
*/
|
||||
function closeScreenOnTabletWindow(): void {
|
||||
window.outerWidth >= 768 && closeScreen()
|
||||
if (window.outerWidth >= 768) {
|
||||
closeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@ -2,7 +2,7 @@ import { computed } from 'vue'
|
||||
import { useData } from './data.js'
|
||||
import { usePostList } from './blog-data.js'
|
||||
|
||||
export function useBlogPost() {
|
||||
export function useBlogPageData() {
|
||||
const { page } = useData()
|
||||
const postList = usePostList()
|
||||
|
||||
@ -10,7 +10,13 @@ export function useBlogPost() {
|
||||
return postList.value.some(item => item.path === page.value.path)
|
||||
})
|
||||
|
||||
const isBlogLayout = computed(() => {
|
||||
const type = page.value.type
|
||||
return type === 'blog' || type === 'blog-archives' || type === 'blog-tags' || type === 'blog-categories'
|
||||
})
|
||||
|
||||
return {
|
||||
isBlogPost,
|
||||
isBlogLayout,
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import { resolveNavLink } from '../utils/index.js'
|
||||
import { usePostList } from './blog-data.js'
|
||||
import { useSidebar } from './sidebar.js'
|
||||
import { useData } from './data.js'
|
||||
import { useBlogPost } from './page.js'
|
||||
import { useBlogPageData } from './page.js'
|
||||
|
||||
export function usePrevNext() {
|
||||
const route = useRoute()
|
||||
@ -15,7 +15,7 @@ export function usePrevNext() {
|
||||
const { sidebar } = useSidebar()
|
||||
const postList = usePostList() as unknown as Ref<PlumeThemeBlogPostItem[]>
|
||||
const locale = usePageLang()
|
||||
const { isBlogPost } = useBlogPost()
|
||||
const { isBlogPost } = useBlogPageData()
|
||||
|
||||
const prevNavList = computed(() => {
|
||||
const prevConfig = resolveFromFrontmatterConfig(frontmatter.value.prev)
|
||||
|
||||
@ -295,7 +295,12 @@ export function useSidebar(): UseSidebarReturn {
|
||||
}
|
||||
|
||||
const toggle = (): void => {
|
||||
isOpen.value ? close() : open()
|
||||
if (isOpen.value) {
|
||||
close()
|
||||
}
|
||||
else {
|
||||
open()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
@ -386,7 +391,9 @@ export function useSidebarControl(item: ComputedRef<ResolvedSidebarItem>): Sideb
|
||||
})
|
||||
|
||||
watchPostEffect(() => {
|
||||
;(isActiveLink.value || hasActiveLink.value) && (collapsed.value = false)
|
||||
if (isActiveLink.value || hasActiveLink.value) {
|
||||
collapsed.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const toggle = (): void => {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import './styles/index.css'
|
||||
|
||||
import { defineClientConfig } from 'vuepress/client'
|
||||
import type { ClientConfig } from 'vuepress/client'
|
||||
import {
|
||||
enhanceScrollBehavior,
|
||||
setupDarkMode,
|
||||
@ -29,4 +28,4 @@ export default defineClientConfig({
|
||||
setupWatermark()
|
||||
},
|
||||
layouts: { Layout, NotFound },
|
||||
}) as ClientConfig
|
||||
})
|
||||
|
||||
@ -133,6 +133,12 @@ useCloseSidebarOnEscape(isSidebarOpen, closeSidebar)
|
||||
<template #blog-tags-after>
|
||||
<slot name="blog-tags-after" />
|
||||
</template>
|
||||
<template #blog-categories-before>
|
||||
<slot name="blog-categories-before" />
|
||||
</template>
|
||||
<template #blog-categories-after>
|
||||
<slot name="blog-categories-after" />
|
||||
</template>
|
||||
<template #blog-post-list-before>
|
||||
<slot name="blog-post-list-before" />
|
||||
</template>
|
||||
|
||||
@ -45,7 +45,13 @@
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.vp-doc h4,
|
||||
.vp-doc h4 {
|
||||
margin: 24px 0 16px;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.vp-doc h5,
|
||||
.vp-doc h6 {
|
||||
margin: 24px 0 16px;
|
||||
@ -127,15 +133,14 @@
|
||||
.vp-doc blockquote {
|
||||
padding-left: 16px;
|
||||
margin: 16px 0;
|
||||
color: var(--vp-c-text-2);
|
||||
border-left: 2px solid var(--vp-c-divider);
|
||||
transition: border-color var(--t-color);
|
||||
transition: border-color var(--t-color), color var(--t-color);
|
||||
}
|
||||
|
||||
.vp-doc blockquote > p {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: var(--vp-c-text-2);
|
||||
transition: color var(--t-color);
|
||||
}
|
||||
|
||||
.vp-doc a {
|
||||
@ -258,7 +263,8 @@
|
||||
|
||||
.vp-doc h1 > code,
|
||||
.vp-doc h2 > code,
|
||||
.vp-doc h3 > code {
|
||||
.vp-doc h3 > code,
|
||||
.vp-doc h4 > code {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
|
||||
@ -266,6 +266,26 @@
|
||||
font-optical-sizing: auto;
|
||||
}
|
||||
|
||||
:root:where(:lang(zh)),
|
||||
:root:where(:lang(zh-CN)) {
|
||||
--vp-font-family-base:
|
||||
"Punctuation SC",
|
||||
"Inter",
|
||||
ui-sans-serif,
|
||||
system-ui,
|
||||
"PingFang SC",
|
||||
"Noto Sans CJK SC",
|
||||
"Noto Sans SC",
|
||||
"Heiti SC",
|
||||
"Microsoft YaHei",
|
||||
"DengXian",
|
||||
sans-serif,
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol",
|
||||
"Noto Color Emoji";
|
||||
}
|
||||
|
||||
/**
|
||||
* Shadows
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
@ -57,8 +57,8 @@ export function scrollTo(
|
||||
const change = top - currentTop
|
||||
const timer = setInterval(() => {
|
||||
currentStep++
|
||||
if (currentStep >= step)
|
||||
timer && clearInterval(timer)
|
||||
if (currentStep >= step && timer)
|
||||
clearInterval(timer)
|
||||
|
||||
setScrollTop(target, tween(currentStep, currentTop, change, step))
|
||||
}, 1000 / 60)
|
||||
|
||||
@ -177,10 +177,12 @@ export function resolveOptions(
|
||||
if (note && sidebar && sidebar !== 'auto') {
|
||||
const res = resolveLinkBySidebar(sidebar, pathJoin(notes?.dir || '', note.dir || ''))
|
||||
const file = ensureLeadingSlash(relativePath)
|
||||
if (res[file])
|
||||
if (res[file]) {
|
||||
args.push(res[file])
|
||||
else
|
||||
res[path.dirname(file)] && args.push(res[path.dirname(file)])
|
||||
}
|
||||
else if (res[path.dirname(file)]) {
|
||||
args.push(res[path.dirname(file)])
|
||||
}
|
||||
}
|
||||
|
||||
return pathJoin(...args, nanoid(), '/')
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { addViteConfig, addViteOptimizeDepsExclude, addViteOptimizeDepsInclude, addViteSsrNoExternal } from '@vuepress/helper'
|
||||
import {
|
||||
addViteConfig,
|
||||
addViteOptimizeDepsExclude,
|
||||
addViteOptimizeDepsInclude,
|
||||
addViteSsrNoExternal,
|
||||
chainWebpack,
|
||||
} from '@vuepress/helper'
|
||||
import type { App } from 'vuepress'
|
||||
|
||||
export function extendsBundlerOptions(bundlerOptions: any, app: App): void {
|
||||
@ -16,4 +22,18 @@ export function extendsBundlerOptions(bundlerOptions: any, app: App): void {
|
||||
'@vuepress/plugin-reading-time',
|
||||
'@vuepress/plugin-watermark',
|
||||
])
|
||||
|
||||
chainWebpack(bundlerOptions, app, (config) => {
|
||||
config.module
|
||||
.rule('scss')
|
||||
.use('sass-loader')
|
||||
.tap((options: any) => ({
|
||||
api: 'modern-compiler',
|
||||
...options,
|
||||
sassOptions: {
|
||||
silenceDeprecations: ['mixed-decls'],
|
||||
...options.sassOptions,
|
||||
},
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
@ -51,14 +51,18 @@ export function resolveThemeData(app: App, options: PlumeThemeLocaleOptions): Pl
|
||||
text: PRESET_LOCALES[localePath].blog,
|
||||
link: withBase(blogLink, locale),
|
||||
})
|
||||
blog.tags !== false && navbar.push({
|
||||
text: PRESET_LOCALES[localePath].tag,
|
||||
link: withBase(blog.tagsLink || `${blogLink}/tags/`, locale),
|
||||
})
|
||||
blog.archives !== false && navbar.push({
|
||||
text: PRESET_LOCALES[localePath].archive,
|
||||
link: withBase(blog.archivesLink || `${blogLink}/archives/`, locale),
|
||||
})
|
||||
if (blog.tags !== false) {
|
||||
navbar.push({
|
||||
text: PRESET_LOCALES[localePath].tag,
|
||||
link: withBase(blog.tagsLink || `${blogLink}/tags/`, locale),
|
||||
})
|
||||
}
|
||||
if (blog.archives !== false) {
|
||||
navbar.push({
|
||||
text: PRESET_LOCALES[localePath].archive,
|
||||
link: withBase(blog.archivesLink || `${blogLink}/archives/`, locale),
|
||||
})
|
||||
}
|
||||
|
||||
themeData.locales![locale].navbar = navbar
|
||||
}
|
||||
|
||||
@ -49,7 +49,9 @@ export async function extendsMarkdown(md: Markdown, app: App): Promise<void> {
|
||||
const update = (filepath: string, data: CacheData): void => {
|
||||
writeFile(`${basename}/${filepath}`, data)
|
||||
|
||||
timer && clearTimeout(timer)
|
||||
if (timer) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
timer = setTimeout(async () => writeFile(metaFilepath, metadata), 200)
|
||||
}
|
||||
const rawRender = md.render
|
||||
|
||||
@ -58,7 +58,9 @@ export async function initConfigLoader(
|
||||
|
||||
loader.configFile = await findConfigPath(app, configFile)
|
||||
|
||||
onChange && loader.changeEvents.push(onChange)
|
||||
if (onChange) {
|
||||
loader.changeEvents.push(onChange)
|
||||
}
|
||||
|
||||
const { config, dependencies = [] } = await loader.load()
|
||||
loader.loaded = true
|
||||
@ -103,7 +105,9 @@ export function watchConfigFile(app: App, watchers: any[]) {
|
||||
export async function onConfigChange(onChange: ChangeEvent) {
|
||||
if (loader && !loader.changeEvents.includes(onChange)) {
|
||||
loader.changeEvents.push(onChange)
|
||||
loader.loaded && onChange(loader.resolvedConfig)
|
||||
if (loader.loaded) {
|
||||
onChange(loader.resolvedConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +46,9 @@ export function genCode(app: App): { js: string, css: string } {
|
||||
const { frontmatter: { tags } } = page
|
||||
if (tags) {
|
||||
toArray(tags).forEach((tag) => {
|
||||
tag && tagList.add(tag as string)
|
||||
if (tag) {
|
||||
tagList.add(tag as string)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -69,7 +69,9 @@ export async function preparedBlogData(
|
||||
excerpt: '',
|
||||
}
|
||||
|
||||
isEncryptPage(page, encrypt) && (data.encrypt = true)
|
||||
if (isEncryptPage(page, encrypt)) {
|
||||
data.encrypt = true
|
||||
}
|
||||
|
||||
if (page.contentRendered.includes(EXCERPT_SPLIT)) {
|
||||
const contents = page.contentRendered.split(EXCERPT_SPLIT)
|
||||
|
||||
@ -38,18 +38,23 @@ function getSidebarData(
|
||||
else if (isPlainObject(sidebar)) {
|
||||
entries(sidebar).forEach(([dirname, config]) => {
|
||||
const prefix = normalizeLink(localePath, dirname)
|
||||
config === 'auto'
|
||||
? autoDirList.push(prefix)
|
||||
: isArray(config)
|
||||
? autoDirList.push(...findAutoDirList(config, prefix))
|
||||
: config.items === 'auto'
|
||||
? autoDirList.push(normalizeLink(prefix, config.prefix))
|
||||
: autoDirList.push(
|
||||
...findAutoDirList(
|
||||
config.items || [],
|
||||
normalizeLink(prefix, config.prefix),
|
||||
),
|
||||
)
|
||||
if (config === 'auto') {
|
||||
autoDirList.push(prefix)
|
||||
}
|
||||
else if (isArray(config)) {
|
||||
autoDirList.push(...findAutoDirList(config, prefix))
|
||||
}
|
||||
else if (config.items === 'auto') {
|
||||
autoDirList.push(normalizeLink(prefix, config.prefix))
|
||||
}
|
||||
else {
|
||||
autoDirList.push(
|
||||
...findAutoDirList(
|
||||
config.items || [],
|
||||
normalizeLink(prefix, config.prefix),
|
||||
),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
else if (sidebar === 'auto') {
|
||||
@ -121,8 +126,8 @@ function getAutoDirSidebar(
|
||||
if (!isHome) {
|
||||
items.push(current)
|
||||
}
|
||||
else {
|
||||
!parent && items.unshift(current)
|
||||
else if (!parent) {
|
||||
items.unshift(current)
|
||||
}
|
||||
}
|
||||
if (dir.endsWith('.md')) {
|
||||
@ -159,9 +164,8 @@ function findAutoDirList(sidebar: (string | SidebarItem)[], prefix = ''): string
|
||||
if (item.items === 'auto') {
|
||||
list.push(nextPrefix)
|
||||
}
|
||||
else {
|
||||
item.items?.length
|
||||
&& list.push(...findAutoDirList(item.items, nextPrefix))
|
||||
else if (item.items?.length) {
|
||||
list.push(...findAutoDirList(item.items, nextPrefix))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -42,22 +42,28 @@ export async function setupPage(
|
||||
}))
|
||||
|
||||
// 添加 标签页
|
||||
blog.tags !== false && pageList.push(createPage(app, {
|
||||
path: withBase(blog.tagsLink || `${link}/tags/`, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog-tags', title: getTitle(locale, 'tag') },
|
||||
}))
|
||||
if (blog.tags !== false) {
|
||||
pageList.push(createPage(app, {
|
||||
path: withBase(blog.tagsLink || `${link}/tags/`, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog-tags', title: getTitle(locale, 'tag') },
|
||||
}))
|
||||
}
|
||||
|
||||
// 添加归档页
|
||||
blog.archives !== false && pageList.push(createPage(app, {
|
||||
path: withBase(blog.archivesLink || `${link}/archives/`, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog-archives', title: getTitle(locale, 'archive') },
|
||||
}))
|
||||
if (blog.archives !== false) {
|
||||
pageList.push(createPage(app, {
|
||||
path: withBase(blog.archivesLink || `${link}/archives/`, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog-archives', title: getTitle(locale, 'archive') },
|
||||
}))
|
||||
}
|
||||
|
||||
// 添加分类页
|
||||
blog.categories !== false && pageList.push(createPage(app, {
|
||||
path: withBase(blog.categoriesLink || `${link}/categories/`, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog-categories', title: getTitle(locale, 'category') },
|
||||
}))
|
||||
if (blog.categories !== false) {
|
||||
pageList.push(createPage(app, {
|
||||
path: withBase(blog.categoriesLink || `${link}/categories/`, localePath),
|
||||
frontmatter: { lang, _pageLayout: 'blog-categories', title: getTitle(locale, 'category') },
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
app.pages.push(...await Promise.all(pageList))
|
||||
@ -137,7 +143,9 @@ export function autoCategory(
|
||||
const categoryList: PageCategoryData[] = list
|
||||
.map((category, index) => {
|
||||
const match = category.match(RE_CATEGORY) || []
|
||||
!cache[match[2]] && !match[1] && (cache[match[2]] = uuid++)
|
||||
if (!cache[match[2]] && !match[1]) {
|
||||
cache[match[2]] = uuid++
|
||||
}
|
||||
return {
|
||||
id: hash(list.slice(0, index + 1).join('-')).slice(0, 6),
|
||||
sort: Number(match[1] || cache[match[2]]),
|
||||
|
||||
@ -40,7 +40,9 @@ export function plumeTheme(options: PlumeThemeOptions = {}): Theme {
|
||||
configFile,
|
||||
onChange: ({ localeOptions, autoFrontmatter }) => {
|
||||
autoFrontmatter ??= pluginOptions.frontmatter
|
||||
autoFrontmatter !== false && initAutoFrontmatter(localeOptions, autoFrontmatter)
|
||||
if (autoFrontmatter !== false) {
|
||||
initAutoFrontmatter(localeOptions, autoFrontmatter)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user