update: 添加多个插件支持
@ -6,7 +6,7 @@ export default defineUserConfig<PlumeThemeOptions>({
|
||||
lang: 'zh',
|
||||
title: 'Plume Theme',
|
||||
description: '',
|
||||
public: path.resolve(__dirname, '../public'),
|
||||
public: path.resolve(__dirname, 'public'),
|
||||
theme: '@vuepress-plume/vuepress-theme-plume',
|
||||
themeConfig: {
|
||||
logo: 'https://pengzhanbo.cn/g.gif',
|
||||
|
||||
1
docs/.vuepress/public/CNAME
Normal file
@ -0,0 +1 @@
|
||||
pengzhanbo.cn
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
@ -4,3 +4,54 @@ createTime: 2022/04/04 01:48:00
|
||||
author: pengzhanbo
|
||||
permalink: /article/exavsmm1
|
||||
---
|
||||
|
||||
:::: code-group
|
||||
|
||||
::: code-group-item yarn
|
||||
``` bash
|
||||
yarn add
|
||||
```
|
||||
:::
|
||||
|
||||
::: code-group-item npm
|
||||
``` bash
|
||||
npm install
|
||||
```
|
||||
:::
|
||||
|
||||
::::
|
||||
|
||||
::: tip
|
||||
提示
|
||||
:::
|
||||
|
||||
::: info
|
||||
信息
|
||||
:::
|
||||
|
||||
::: note
|
||||
注释
|
||||
:::
|
||||
|
||||
::: warning
|
||||
警告
|
||||
:::
|
||||
|
||||
::: danger
|
||||
危险
|
||||
:::
|
||||
|
||||
::: details
|
||||
详情
|
||||
:::
|
||||
|
||||
|
||||
- [ ] todo
|
||||
- [x] todo
|
||||
|
||||
::: demo 普通代码
|
||||
```html
|
||||
<h3>这是一个代码片段</h3>
|
||||
<span>???</span>
|
||||
```
|
||||
:::
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
---
|
||||
title: CNAME
|
||||
createTime: 2022/04/06 12:46:57
|
||||
author: pengzhanbo
|
||||
permalink: /article/i7k2vzbu/
|
||||
---
|
||||
pengzhanbo.cn
|
||||
@ -1,7 +1,5 @@
|
||||
{
|
||||
"name": "vuepress-theme-plume",
|
||||
"version": "1.0.0-beta.10",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@ -25,7 +23,7 @@
|
||||
"license": "MIT",
|
||||
"prettier": "prettier-config-vuepress",
|
||||
"devDependencies": {
|
||||
"@vuepress/cli": "^2.0.0-beta.37",
|
||||
"@vuepress/cli": "^2.0.0-beta.38",
|
||||
"concurrently": "^7.0.0",
|
||||
"cpx2": "^4.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
@ -37,8 +35,8 @@
|
||||
"prettier-config-vuepress": "^1.3.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"typescript": "^4.6.3",
|
||||
"vuepress-vite": "^2.0.0-beta.37",
|
||||
"vuepress-webpack": "^2.0.0-beta.37",
|
||||
"vuepress-vite": "^2.0.0-beta.38",
|
||||
"vuepress-webpack": "^2.0.0-beta.38",
|
||||
"webpack-env": "^0.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@ -3,12 +3,19 @@
|
||||
"version": "1.0.0-beta.6",
|
||||
"description": "The Plugin for VuePres 2, Support Can-I-Use feature",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"homepage": "",
|
||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||
"license": "MIT",
|
||||
"main": "lib/node/index.js",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"ts": "tsc -b tsconfig.build.json",
|
||||
"ts:watch": "tsc -b tsconfig.build.json --watch",
|
||||
@ -17,11 +24,14 @@
|
||||
"build": "yarn clean && yarn ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vuepress/client": "^2.0.0-beta.37",
|
||||
"@vuepress/core": "^2.0.0-beta.37",
|
||||
"@vuepress/utils": "^2.0.0-beta.37",
|
||||
"@vuepress/client": "^2.0.0-beta.38",
|
||||
"@vuepress/core": "^2.0.0-beta.38",
|
||||
"@vuepress/utils": "^2.0.0-beta.38",
|
||||
"markdown-it-container": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vuepress": "^2.0.0-beta.38"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
|
||||
@ -3,13 +3,20 @@
|
||||
"version": "1.0.0-beta.7",
|
||||
"description": "A Blog Theme for VuePress 2.0",
|
||||
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
||||
"homepage": "",
|
||||
"homepage": "https://github.com/pengzhanbo/vuepress-theme-plume#readme",
|
||||
"license": "MIT",
|
||||
"main": "lib/node/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"template"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pengzhanbo/vuepress-theme-plume.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/pengzhanbo/vuepress-theme-plume/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"copy": "cpx \"src/**/*.{d.ts,vue,css,scss,jpg,png}\" lib",
|
||||
"copy:watch": "yarn copy -w",
|
||||
@ -22,19 +29,21 @@
|
||||
"dependencies": {
|
||||
"@types/lodash.merge": "^4.6.6",
|
||||
"@vuepress-plume/vuepress-plugin-caniuse": "1.0.0-beta.6",
|
||||
"@vuepress/client": "^2.0.0-beta.37",
|
||||
"@vuepress/core": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-container": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-docsearch": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-external-link-icon": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-medium-zoom": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-nprogress": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-prismjs": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-search": "^2.0.0-beta.36",
|
||||
"@vuepress/plugin-theme-data": "^2.0.0-beta.37",
|
||||
"@vuepress/plugin-toc": "^2.0.0-beta.37",
|
||||
"@vuepress/shared": "^2.0.0-beta.37",
|
||||
"@vuepress/utils": "^2.0.0-beta.37",
|
||||
"@vuepress/client": "^2.0.0-beta.38",
|
||||
"@vuepress/core": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-active-header-links": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-container": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-docsearch": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-external-link-icon": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-medium-zoom": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-nprogress": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-palette": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-prismjs": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-search": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-theme-data": "^2.0.0-beta.38",
|
||||
"@vuepress/plugin-toc": "^2.0.0-beta.38",
|
||||
"@vuepress/shared": "^2.0.0-beta.38",
|
||||
"@vuepress/utils": "^2.0.0-beta.38",
|
||||
"@vueuse/core": "^8.2.3",
|
||||
"chokidar": "^3.5.3",
|
||||
"date-fns": "^2.28.0",
|
||||
@ -45,7 +54,12 @@
|
||||
"sass": "^1.49.9",
|
||||
"sass-loader": "^12.6.0",
|
||||
"vue": "^3.2.31",
|
||||
"vue-router": "^4.0.14"
|
||||
"vue-router": "^4.0.14",
|
||||
"vuepress-plugin-copy-code2": "^2.0.0-beta.36",
|
||||
"vuepress-plugin-md-enhance": "^2.0.0-beta.36",
|
||||
"vuepress-plugin-reading-time2": "^2.0.0-beta.36",
|
||||
"vuepress-plugin-seo2": "^2.0.0-beta.36",
|
||||
"vuepress-plugin-sitemap2": "^2.0.0-beta.36"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -12,11 +12,4 @@ export default defineClientAppEnhance(({ app }) => {
|
||||
}
|
||||
return null
|
||||
})
|
||||
app.component('Toc', () => {
|
||||
const Toc = app.component('TocCom')
|
||||
if (Toc) {
|
||||
return h(Toc)
|
||||
}
|
||||
return null
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,27 +1,54 @@
|
||||
<script lang="ts" setup>
|
||||
import BlogInfo from '@theme-plume/BlogInfo.vue'
|
||||
import DropdownTransition from '@theme-plume/DropdownTransition.vue'
|
||||
import type { PageHeader } from '@vuepress/client'
|
||||
import { computed } from 'vue'
|
||||
import { useArchive } from '../composables'
|
||||
import Toc from './Toc'
|
||||
|
||||
const archiveList = useArchive()
|
||||
|
||||
const headers = computed(() => {
|
||||
return archiveList.value.map(({ year }) => {
|
||||
return {
|
||||
level: 2,
|
||||
slug: year,
|
||||
title: year,
|
||||
children: [],
|
||||
} as PageHeader
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="archive-wrapper">
|
||||
<div class="archive-container">
|
||||
<DropdownTransition>
|
||||
<div class="archive-content">
|
||||
<div
|
||||
v-for="archive in archiveList"
|
||||
:key="archive.year"
|
||||
class="archive-items"
|
||||
>
|
||||
<h2>{{ archive.year }}</h2>
|
||||
<ul class="archive-list">
|
||||
<li v-for="child in archive.children" :key="child.link">
|
||||
<span>{{ child.date }}</span>
|
||||
<RouterLink :to="child.link">{{ child.text }}</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="archive-box">
|
||||
<div
|
||||
v-for="archive in archiveList"
|
||||
:key="archive.year"
|
||||
class="archive-items"
|
||||
>
|
||||
<h2>
|
||||
<a
|
||||
class="header-anchor"
|
||||
:href="'#' + archive.year"
|
||||
aria-hidden="true"
|
||||
>#</a
|
||||
>
|
||||
{{ archive.year }}
|
||||
</h2>
|
||||
<ul class="archive-list">
|
||||
<li v-for="child in archive.children" :key="child.link">
|
||||
<span>{{ child.date }}</span>
|
||||
<RouterLink :to="child.link">{{ child.text }}</RouterLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archive-toc">
|
||||
<Toc :headers="headers" />
|
||||
</div>
|
||||
</div>
|
||||
</DropdownTransition>
|
||||
@ -43,7 +70,13 @@ const archiveList = useArchive()
|
||||
}
|
||||
|
||||
.archive-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex: 1;
|
||||
|
||||
.archive-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.archive-items {
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
} from '@vuepress/client'
|
||||
import { computed, h } from 'vue'
|
||||
import type { FunctionalComponent } from 'vue'
|
||||
import type { NavLink } from '../../shared'
|
||||
import { useDarkMode, useThemeLocaleData } from '../composables'
|
||||
|
||||
const routeLocale = useRouteLocale()
|
||||
@ -15,7 +16,7 @@ const themeLocale = useThemeLocaleData()
|
||||
const isDarkMode = useDarkMode()
|
||||
|
||||
const navbarBrandLink = computed(
|
||||
() => themeLocale.value.home || routeLocale.value
|
||||
() => (themeLocale.value.home as NavLink)?.link || routeLocale.value
|
||||
)
|
||||
const navbarBrandTitle = computed(() => siteLocale.value.title)
|
||||
const navbarBrandLogo = computed(() => {
|
||||
|
||||
@ -6,6 +6,7 @@ import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { PlumeThemePageData } from '../../shared'
|
||||
import { useThemeLocaleData } from '../composables'
|
||||
import Toc from './Toc'
|
||||
const page = usePageData<PlumeThemePageData>()
|
||||
const route = useRoute()
|
||||
const themeLocale = useThemeLocaleData()
|
||||
|
||||
147
packages/theme/src/client/components/Toc.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import { usePageData } from '@vuepress/client'
|
||||
import type { PageHeader } from '@vuepress/client'
|
||||
import type { PropType, VNode } from 'vue'
|
||||
import { computed, defineComponent, h, toRefs } from 'vue'
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export type TocPropsHeaders = PageHeader[]
|
||||
|
||||
export interface TocPropsOptions {
|
||||
containerTag: string
|
||||
containerClass: string
|
||||
listClass: string
|
||||
itemClass: string
|
||||
linkClass: string
|
||||
linkActiveClass: string
|
||||
linkChildrenActiveClass: string
|
||||
}
|
||||
|
||||
export interface TocProps {
|
||||
headers: TocPropsHeaders
|
||||
options: TocPropsOptions
|
||||
}
|
||||
|
||||
const renderLink = (
|
||||
header: PageHeader,
|
||||
options: TocPropsOptions,
|
||||
route: RouteLocationNormalizedLoaded
|
||||
): VNode => {
|
||||
const hash = `#${header.slug}`
|
||||
const linkClass = [options.linkClass]
|
||||
|
||||
if (options.linkActiveClass && route.hash === hash) {
|
||||
linkClass.push(options.linkActiveClass)
|
||||
}
|
||||
|
||||
if (
|
||||
options.linkChildrenActiveClass &&
|
||||
header.children.some((item) => `#${item.slug}` === route.hash)
|
||||
) {
|
||||
linkClass.push(options.linkChildrenActiveClass)
|
||||
}
|
||||
|
||||
const setActiveRouteHash = (): void => {
|
||||
const headerAnchors: HTMLAnchorElement[] = Array.from(
|
||||
document.querySelectorAll('.header-anchor')
|
||||
)
|
||||
const anchor = headerAnchors.find(
|
||||
(anchor) => decodeURI(anchor.hash) === hash
|
||||
)
|
||||
if (!anchor) return
|
||||
const el = document.documentElement
|
||||
const top = anchor.getBoundingClientRect().top - 80 + el.scrollTop
|
||||
el.scrollTo ? el.scrollTo({ top }) : (el.scrollTop = top)
|
||||
}
|
||||
|
||||
return h(
|
||||
'a',
|
||||
{
|
||||
href: hash,
|
||||
class: linkClass,
|
||||
ariaLabel: header.title,
|
||||
onClick: (e) => {
|
||||
e.preventDefault()
|
||||
setActiveRouteHash()
|
||||
},
|
||||
},
|
||||
header.title
|
||||
)
|
||||
}
|
||||
|
||||
const renderHeaders = (
|
||||
headers: PageHeader[],
|
||||
options: TocPropsOptions,
|
||||
route: RouteLocationNormalizedLoaded
|
||||
): VNode[] => {
|
||||
if (headers.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [
|
||||
h(
|
||||
'ul',
|
||||
{ class: options.listClass },
|
||||
headers.map((header) =>
|
||||
h('li', { class: options.itemClass }, [
|
||||
renderLink(header, options, route),
|
||||
renderHeaders(header.children, options, route),
|
||||
])
|
||||
)
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
const Toc = defineComponent({
|
||||
name: 'Toc',
|
||||
props: {
|
||||
headers: {
|
||||
type: Array as PropType<TocPropsHeaders>,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
options: {
|
||||
type: Object as PropType<TocPropsOptions>,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const { headers: propsHeaders, options: propsOptions } = toRefs(props)
|
||||
|
||||
const defaultOptions: TocPropsOptions = {
|
||||
containerTag: 'nav',
|
||||
containerClass: 'theme-plume-toc',
|
||||
listClass: 'theme-plume-toc-list',
|
||||
itemClass: 'theme-plume-toc-item',
|
||||
linkClass: 'theme-plume-toc-link',
|
||||
linkActiveClass: 'active',
|
||||
linkChildrenActiveClass: 'active',
|
||||
}
|
||||
|
||||
const route = useRoute()
|
||||
const page = usePageData()
|
||||
const headers = computed<TocPropsHeaders>(() => {
|
||||
const headerToUse = propsHeaders.value || page.value.headers
|
||||
|
||||
return headerToUse[0]?.level === 1 ? headerToUse[0].children : headerToUse
|
||||
})
|
||||
const options = computed<TocPropsOptions>(() => ({
|
||||
...defaultOptions,
|
||||
...propsOptions.value,
|
||||
}))
|
||||
|
||||
return () => {
|
||||
const renderedHeaders = renderHeaders(headers.value, options.value, route)
|
||||
if (options.value.containerTag) {
|
||||
return h(
|
||||
options.value.containerTag,
|
||||
{ class: options.value.containerClass },
|
||||
renderedHeaders
|
||||
)
|
||||
}
|
||||
return renderedHeaders
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default Toc
|
||||
@ -7,7 +7,9 @@ const themeLocale = useThemeLocaleData()
|
||||
|
||||
const message = themeLocale.value.notFound ?? ['Not Found']
|
||||
const getMsg = (): string => message[Math.floor(Math.random() * message.length)]
|
||||
const homeLink = themeLocale.value.home ?? routeLocale.value
|
||||
const homeLink = themeLocale.value.home
|
||||
? themeLocale.value.home.link
|
||||
: routeLocale.value
|
||||
const homeText = themeLocale.value.backToHome ?? 'Back to home'
|
||||
</script>
|
||||
<template>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
@import '@vuepress/plugin-palette/palette';
|
||||
|
||||
$MQNarrow: 959px !default;
|
||||
$MQMobile: 719px !default;
|
||||
$MQMobileNarrow: 419px !default;
|
||||
|
||||
@ -9,3 +9,5 @@
|
||||
@use 'layout';
|
||||
@use 'code';
|
||||
@use 'toc';
|
||||
|
||||
@use'@vuepress/plugin-palette/style';
|
||||
|
||||
@ -7,3 +7,7 @@
|
||||
padding-bottom: 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
.task-list-container {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@ -1,54 +1,65 @@
|
||||
.plume-theme-page-toc {
|
||||
width: 200px;
|
||||
margin-left: 1.25rem;
|
||||
|
||||
.vuepress-toc {
|
||||
position: sticky;
|
||||
top: calc(var(--navbar-height) + 1.25rem);
|
||||
border-left: solid 1px var(--c-border);
|
||||
max-height: calc(100vh - var(--navbar-height) - (1.25rem * 3));
|
||||
overflow-y: auto;
|
||||
.theme-plume-toc {
|
||||
border-left: solid 1px var(--c-border);
|
||||
max-height: calc(100vh - var(--navbar-height) - (1.25rem * 3));
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
> .vuepress-toc-list {
|
||||
padding-left: 0;
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
> .theme-plume-toc-list {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.theme-plume-toc-list {
|
||||
list-style: none;
|
||||
|
||||
.theme-plume-toc-link {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
color: var(--c-text-light);
|
||||
border-left: solid 3px transparent;
|
||||
padding-left: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: color var(--t-color), border-color var(--t-color);
|
||||
|
||||
&.active {
|
||||
color: var(--c-text-accent);
|
||||
border-color: var((--c-text-accent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.vuepress-toc-list {
|
||||
list-style: none;
|
||||
|
||||
.vuepress-toc-link {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
color: var(--c-text-light);
|
||||
border-left: solid 3px transparent;
|
||||
padding-left: 1rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
transition: color var(--t-color), border-color var(--t-color);
|
||||
.theme-plume-toc-list {
|
||||
.theme-plume-toc-link {
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
color: var(--c-text-lighter);
|
||||
|
||||
&.active {
|
||||
color: var(--c-text-accent);
|
||||
border-color: var((--c-text-accent));
|
||||
}
|
||||
}
|
||||
|
||||
.vuepress-toc-list {
|
||||
.vuepress-toc-link {
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
color: var(--c-text-lighter);
|
||||
|
||||
&.active {
|
||||
color: var(--c-text-accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.plume-theme-page-toc {
|
||||
width: 200px;
|
||||
margin-left: 1.25rem;
|
||||
|
||||
.theme-plume-toc {
|
||||
position: sticky;
|
||||
top: calc(var(--navbar-height) + 1.25rem);
|
||||
}
|
||||
}
|
||||
|
||||
.archive-toc {
|
||||
width: 4rem;
|
||||
margin-left: 0;
|
||||
position: sticky;
|
||||
top: calc(var(--navbar-height) + 1.25rem);
|
||||
}
|
||||
|
||||
14
packages/theme/src/node/plugins/activeHeaderLink.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
import type { ActiveHeaderLinksPluginOptions } from '@vuepress/plugin-active-header-links'
|
||||
|
||||
export const resolveActiveHeaderLink = (): PluginConfig => {
|
||||
return [
|
||||
'@vuepress/active-header-links',
|
||||
{
|
||||
headerLinkSelector: 'a.theme-plume-toc-link',
|
||||
headerAnchorSelector: '.header-anchor',
|
||||
delay: 200,
|
||||
offset: 80,
|
||||
} as ActiveHeaderLinksPluginOptions,
|
||||
]
|
||||
}
|
||||
@ -1,9 +1,14 @@
|
||||
import type { CanIUsePluginOptions } from '@vuepress-plume/vuepress-plugin-caniuse'
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
import type { PlumeThemePluginOptions } from '../../shared'
|
||||
|
||||
export const resolveCanIUse = (
|
||||
options: Partial<CanIUsePluginOptions> | false
|
||||
plugins: PlumeThemePluginOptions
|
||||
): PluginConfig => {
|
||||
if (options === false) return ['', false]
|
||||
return ['@vuepress-plume/caniuse', options]
|
||||
if (plugins.caniuse === false) return ['', false]
|
||||
return [
|
||||
'@vuepress-plume/caniuse',
|
||||
plugins.caniuse || {
|
||||
mode: 'embed',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
21
packages/theme/src/node/plugins/copyCode.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
import { copyCode } from 'vuepress-plugin-copy-code2'
|
||||
import type { PlumeThemePluginOptions } from '../../shared'
|
||||
|
||||
export const resolveCopyCode = (
|
||||
plugins: PlumeThemePluginOptions
|
||||
): PluginConfig => {
|
||||
if (plugins.copyCode === false) return ['', false]
|
||||
|
||||
return copyCode(
|
||||
plugins.copyCode || {
|
||||
selector: '.page-content div[class*="language-"] pre',
|
||||
locales: {
|
||||
'/': {
|
||||
copy: '复制成功',
|
||||
hint: '复制代码',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -1,12 +1,15 @@
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
import type { ExternalLinkIconPluginOptions } from '@vuepress/plugin-external-link-icon'
|
||||
import type { PlumeThemeLocaleOptions } from '../../shared'
|
||||
import type {
|
||||
PlumeThemeLocaleOptions,
|
||||
PlumeThemePluginOptions,
|
||||
} from '../../shared'
|
||||
|
||||
export const resolveExternalLinkIconPlugin = (
|
||||
enabled: boolean | undefined,
|
||||
plugins: PlumeThemePluginOptions,
|
||||
localeOptions: PlumeThemeLocaleOptions
|
||||
): PluginConfig => {
|
||||
if (enabled === false) return ['', false]
|
||||
if (plugins.externalLinkIcon === false) return ['', false]
|
||||
return [
|
||||
'@vuepress/plugin-external-link-icon',
|
||||
{
|
||||
|
||||
@ -3,14 +3,17 @@ import type {
|
||||
PlumeThemeLocaleOptions,
|
||||
PlumeThemePluginOptions,
|
||||
} from '../../shared'
|
||||
import { resolveActiveHeaderLink } from './activeHeaderLink'
|
||||
import { resolveCanIUse } from './caniuse'
|
||||
import { resolveCopyCode } from './copyCode'
|
||||
import { resolveExternalLinkIconPlugin } from './externalLinkIcon'
|
||||
import { resolveMarkdownEnhance } from './markdownEnhance'
|
||||
import { resolveMediumZoom } from './mediumZoom'
|
||||
import { resolveNprogress } from './nprogress'
|
||||
import { resolvePalette } from './palette'
|
||||
import { resolvePrismjs } from './prismjs'
|
||||
import { resolveSearch } from './search'
|
||||
import { resolveThemeData } from './themeData'
|
||||
import { resolveToc } from './toc'
|
||||
|
||||
export const getPlugins = (
|
||||
app: App,
|
||||
@ -18,13 +21,16 @@ export const getPlugins = (
|
||||
localeOptions: PlumeThemeLocaleOptions
|
||||
): PluginConfig<PluginOptions>[] => {
|
||||
return [
|
||||
resolveActiveHeaderLink(),
|
||||
resolvePalette(),
|
||||
resolveNprogress(plugins),
|
||||
resolveMediumZoom(plugins),
|
||||
resolveToc(plugins),
|
||||
resolveCanIUse(plugins.caniuse || false),
|
||||
resolveExternalLinkIconPlugin(plugins.externalLinkIcon, localeOptions),
|
||||
resolveCanIUse(plugins),
|
||||
resolveExternalLinkIconPlugin(plugins, localeOptions),
|
||||
resolveSearch(plugins),
|
||||
resolvePrismjs(plugins),
|
||||
resolveCopyCode(plugins),
|
||||
resolveMarkdownEnhance(plugins),
|
||||
resolveThemeData(localeOptions),
|
||||
].filter((item) => item[1] !== false)
|
||||
}
|
||||
|
||||
19
packages/theme/src/node/plugins/markdownEnhance.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
import { mdEnhance } from 'vuepress-plugin-md-enhance'
|
||||
import type { PlumeThemePluginOptions } from '../../shared'
|
||||
|
||||
export const resolveMarkdownEnhance = (
|
||||
plugins: PlumeThemePluginOptions
|
||||
): PluginConfig => {
|
||||
if (plugins.markdownEnhance === false) return ['', false]
|
||||
return mdEnhance(
|
||||
plugins.markdownEnhance || {
|
||||
container: true, // info note tip warning danger details
|
||||
codegroup: true,
|
||||
align: true,
|
||||
mark: true,
|
||||
tasklist: true,
|
||||
demo: true,
|
||||
}
|
||||
)
|
||||
}
|
||||
5
packages/theme/src/node/plugins/palette.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
|
||||
export const resolvePalette = (): PluginConfig => {
|
||||
return ['@vuepress/plugin-palette', { preset: 'sass' }]
|
||||
}
|
||||
@ -24,14 +24,14 @@ export const resolveSearch = (
|
||||
plugins: PlumeThemePluginOptions
|
||||
): PluginConfig => {
|
||||
if (plugins.search) {
|
||||
if (!hasSearchInstalled()) {
|
||||
if (!hasSearchInstalled() || plugins.search === false) {
|
||||
logger.error('@vuepress/plugin-search is not installed.')
|
||||
return ['', false]
|
||||
}
|
||||
return ['@vuepress/search', plugins.search]
|
||||
}
|
||||
if (plugins.docsearch) {
|
||||
if (!hasDocsearchInstalled()) {
|
||||
if (!hasDocsearchInstalled() || plugins.docsearch === false) {
|
||||
logger.error('@vuepress/plugin-docsearch is not installed.')
|
||||
return ['', false]
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
import type { PluginConfig } from '@vuepress/core'
|
||||
import type { TocPluginOptions } from '@vuepress/plugin-toc'
|
||||
import type { PlumeThemePluginOptions } from '../../shared'
|
||||
|
||||
export const resolveToc = (plugins: PlumeThemePluginOptions): PluginConfig => {
|
||||
if (plugins.toc === false) return ['', false]
|
||||
return [
|
||||
'@vuepress/toc',
|
||||
{
|
||||
componentName: 'TocCom',
|
||||
defaultPropsOptions: {},
|
||||
} as TocPluginOptions,
|
||||
]
|
||||
}
|
||||
@ -1,32 +1,36 @@
|
||||
import type { CanIUsePluginOptions } from '@vuepress-plume/vuepress-plugin-caniuse'
|
||||
import type { DocsearchOptions } from '@vuepress/plugin-docsearch'
|
||||
import type { SearchPluginOptions } from '@vuepress/plugin-search'
|
||||
import type { CopyCodeOptions } from 'vuepress-plugin-copy-code2'
|
||||
import type { MarkdownEnhanceOptions } from 'vuepress-plugin-md-enhance'
|
||||
export interface PlumeThemePluginOptions {
|
||||
/**
|
||||
* 是否启用 can-i-use 插件
|
||||
*/
|
||||
caniuse?: CanIUsePluginOptions
|
||||
caniuse?: false | CanIUsePluginOptions
|
||||
|
||||
/**
|
||||
* 是否启用 external-link-icon 插件
|
||||
*/
|
||||
externalLinkIcon?: boolean
|
||||
externalLinkIcon?: false
|
||||
|
||||
/**
|
||||
* plugin-search 配置
|
||||
*/
|
||||
search?: Partial<SearchPluginOptions>
|
||||
search?: false | Partial<SearchPluginOptions>
|
||||
|
||||
/**
|
||||
* plugin-docsearch 配置
|
||||
*/
|
||||
docsearch?: Partial<DocsearchOptions>
|
||||
docsearch?: false | Partial<DocsearchOptions>
|
||||
|
||||
prismjs?: boolean
|
||||
prismjs?: false
|
||||
|
||||
nprogress?: boolean
|
||||
nprogress?: false
|
||||
|
||||
mediumZoom?: boolean
|
||||
mediumZoom?: false
|
||||
|
||||
toc?: boolean
|
||||
copyCode?: false | CopyCodeOptions
|
||||
|
||||
markdownEnhance?: false | MarkdownEnhanceOptions
|
||||
}
|
||||
|
||||