mirror of
https://github.com/pengzhanbo/vuepress-theme-plume.git
synced 2026-04-23 10:58:13 +08:00
feat(plugin-md-power): add <Plot /> and =||= syntax
This commit is contained in:
parent
7daf764fa0
commit
9a5eda49a8
@ -13,7 +13,7 @@ export const theme: Theme = themePlume({
|
||||
url: '/plume.png',
|
||||
name: 'Plume Theme',
|
||||
description: 'The Theme for Vuepress 2.0',
|
||||
location: 'Guangzhou, China',
|
||||
location: 'GuangZhou, China',
|
||||
organization: 'pengzhanbo',
|
||||
},
|
||||
|
||||
@ -40,6 +40,7 @@ export const theme: Theme = themePlume({
|
||||
{ icon: 'steam', link: 'https://pengzhanbo.cn' },
|
||||
{ icon: 'xbox', link: 'https://pengzhanbo.cn' },
|
||||
],
|
||||
navbarSocialInclude: ['github'],
|
||||
|
||||
watermark: {
|
||||
global: false,
|
||||
@ -74,6 +75,7 @@ export const theme: Theme = themePlume({
|
||||
markdownPower: {
|
||||
pdf: true,
|
||||
caniuse: true,
|
||||
plot: true,
|
||||
bilibili: true,
|
||||
youtube: true,
|
||||
icons: true,
|
||||
|
||||
97
plugins/plugin-md-power/src/client/components/Plot.vue
Normal file
97
plugins/plugin-md-power/src/client/components/Plot.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, shallowRef } from 'vue'
|
||||
import { onClickOutside, useMediaQuery } from '@vueuse/core'
|
||||
import { usePageFrontmatter } from 'vuepress/client'
|
||||
import type { PlotOptions } from '../../shared/plot.js'
|
||||
import { pluginOptions } from '../options.js'
|
||||
|
||||
const props = defineProps<Omit<PlotOptions, 'tag'>>()
|
||||
|
||||
const matter = usePageFrontmatter()
|
||||
|
||||
const options = computed(() => {
|
||||
const plot = typeof pluginOptions.plot === 'object' ? pluginOptions.plot : {}
|
||||
|
||||
return {
|
||||
trigger: props.trigger || matter.value.plotTrigger || plot.trigger || 'hover',
|
||||
color: props.color || plot.color,
|
||||
mask: props.mask || plot.mask,
|
||||
}
|
||||
})
|
||||
|
||||
const styles = computed(() => {
|
||||
const plot = options.value
|
||||
if (!plot.color && !plot.mask)
|
||||
return {}
|
||||
const style: Record<string, string> = {}
|
||||
if (plot.color) {
|
||||
if (typeof plot.color === 'string') {
|
||||
style['--vp-c-plot-light'] = plot.color
|
||||
}
|
||||
else {
|
||||
style['--vp-c-plot-light'] = plot.color.light
|
||||
style['--vp-c-plot-dark'] = plot.color.dark
|
||||
}
|
||||
}
|
||||
if (plot.mask) {
|
||||
if (typeof plot.mask === 'string') {
|
||||
style['--vp-c-bg-plot-light'] = plot.mask
|
||||
}
|
||||
else {
|
||||
style['--vp-c-bg-plot-light'] = plot.mask.light
|
||||
style['--vp-c-bg-plot-dark'] = plot.mask.dark
|
||||
}
|
||||
}
|
||||
return style
|
||||
})
|
||||
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
const active = ref(false)
|
||||
const el = shallowRef<HTMLElement>()
|
||||
|
||||
onClickOutside(el, () => {
|
||||
if (options.value.trigger === 'click' || isMobile.value)
|
||||
active.value = false
|
||||
})
|
||||
|
||||
function onClick() {
|
||||
if (props.trigger === 'click' || isMobile.value)
|
||||
active.value = !active.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span
|
||||
ref="el"
|
||||
class="vp-plot"
|
||||
:class="{ hover: options.trigger !== 'click', active }"
|
||||
:style="styles"
|
||||
@click="onClick"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.vp-plot {
|
||||
padding-right: 2px;
|
||||
padding-left: 2px;
|
||||
color: transparent;
|
||||
background-color: var(--vp-c-bg-plot-light, #000);
|
||||
transition: color ease 0.25s, background-color ease 0.25s;
|
||||
}
|
||||
|
||||
.dark .vp-plot {
|
||||
background-color: var(--vp-c-bg-plot-dark, #fff);
|
||||
}
|
||||
|
||||
.vp-plot.hover:hover,
|
||||
.vp-plot.active {
|
||||
color: var(--vp-c-plot-light, #fff);
|
||||
}
|
||||
|
||||
.dark .vp-plot.hover:hover,
|
||||
.dark .vp-plot.active {
|
||||
color: var(--vp-c-plot-dark, #000);
|
||||
}
|
||||
</style>
|
||||
73
plugins/plugin-md-power/src/node/features/plot.ts
Normal file
73
plugins/plugin-md-power/src/node/features/plot.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/**
|
||||
* =|这里的文本将被黑幕隐藏,通过点击或者 hover 才可重现|=
|
||||
*/
|
||||
import type { PluginWithOptions } from 'markdown-it'
|
||||
import type { RuleInline } from 'markdown-it/lib/parser_inline.mjs'
|
||||
|
||||
const [openTag, endTag] = ['=|', '|=']
|
||||
|
||||
function createTokenizer(): RuleInline {
|
||||
return (state, silent) => {
|
||||
let found = false
|
||||
const max = state.posMax
|
||||
const start = state.pos
|
||||
|
||||
if (state.src.slice(start, start + 2) !== openTag)
|
||||
return false
|
||||
|
||||
if (silent)
|
||||
return false
|
||||
|
||||
// =||=
|
||||
if (max - start < 5)
|
||||
return false
|
||||
|
||||
state.pos = start + 2
|
||||
|
||||
while (state.pos < max) {
|
||||
if (state.src.slice(state.pos - 1, state.pos + 1) === endTag) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
|
||||
state.md.inline.skipToken(state)
|
||||
}
|
||||
|
||||
if (!found || start + 2 === state.pos) {
|
||||
state.pos = start
|
||||
|
||||
return false
|
||||
}
|
||||
const content = state.src.slice(start + 2, state.pos - 1)
|
||||
|
||||
// 不允许前后带有空格
|
||||
if (/^\s|\s$/.test(content)) {
|
||||
state.pos = start
|
||||
return false
|
||||
}
|
||||
|
||||
// found!
|
||||
state.posMax = state.pos - 1
|
||||
state.pos = start + 2
|
||||
|
||||
const open = state.push('plot_open', 'Plot', 1)
|
||||
open.markup = openTag
|
||||
|
||||
const text = state.push('text', '', 0)
|
||||
text.content = content
|
||||
|
||||
const close = state.push('plot_close', 'Plot', -1)
|
||||
close.markup = endTag
|
||||
|
||||
state.pos = state.posMax + 2
|
||||
state.posMax = max
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export const plotPlugin: PluginWithOptions<never> = (
|
||||
md,
|
||||
) => {
|
||||
md.inline.ruler.before('emphasis', 'plot', createTokenizer())
|
||||
}
|
||||
@ -6,5 +6,6 @@ export * from './codepen.js'
|
||||
export * from './codeSandbox.js'
|
||||
export * from './replit.js'
|
||||
export * from './jsfiddle.js'
|
||||
export * from './plot.js'
|
||||
|
||||
export * from './plugin.js'
|
||||
|
||||
24
plugins/plugin-md-power/src/shared/plot.ts
Normal file
24
plugins/plugin-md-power/src/shared/plot.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export interface PlotOptions {
|
||||
/**
|
||||
* 是否启用 `=| |=` markdown (该标记为非标准标记,脱离插件将不生效)
|
||||
* @default true
|
||||
*/
|
||||
tag?: boolean
|
||||
|
||||
/**
|
||||
* 遮罩层颜色
|
||||
*/
|
||||
mask?: string | { light: string, dark: string }
|
||||
|
||||
/**
|
||||
* 文本颜色
|
||||
*/
|
||||
color?: string | { light: string, dark: string }
|
||||
|
||||
/**
|
||||
* 触发方式
|
||||
*
|
||||
* @default 'hover'
|
||||
*/
|
||||
trigger?: 'hover' | 'click'
|
||||
}
|
||||
@ -1,12 +1,14 @@
|
||||
import type { CanIUseOptions } from './caniuse.js'
|
||||
import type { PDFOptions } from './pdf.js'
|
||||
import type { IconsOptions } from './icons.js'
|
||||
import type { PlotOptions } from './plot.js'
|
||||
|
||||
export interface MarkdownPowerPluginOptions {
|
||||
pdf?: boolean | PDFOptions
|
||||
|
||||
// new syntax
|
||||
icons?: boolean | IconsOptions
|
||||
plot?: boolean | PlotOptions
|
||||
|
||||
// video embed
|
||||
bilibili?: boolean
|
||||
|
||||
@ -666,3 +666,11 @@ html.dark {
|
||||
--code-bg-color: var(--vp-code-block-bg);
|
||||
--medium-zoom-bg-color: var(--vp-c-bg);
|
||||
}
|
||||
|
||||
/* md power plot */
|
||||
:root {
|
||||
--vp-c-plot-light: var(--vp-c-bg);
|
||||
--vp-c-bg-plot-light: var(--vp-c-text-1);
|
||||
--vp-c-plot-dark: var(--vp-c-bg);
|
||||
--vp-c-bg-plot-dark: var(--vp-c-text-2);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import { iconifyPlugin } from '@vuepress-plume/plugin-iconify'
|
||||
import { notesDataPlugin } from '@vuepress-plume/plugin-notes-data'
|
||||
import { shikiPlugin } from '@vuepress-plume/plugin-shikiji'
|
||||
import { commentPlugin } from '@vuepress/plugin-comment'
|
||||
import { type MarkdownEnhanceOptions, mdEnhancePlugin } from 'vuepress-plugin-md-enhance'
|
||||
import { type MarkdownEnhancePluginOptions, mdEnhancePlugin } from 'vuepress-plugin-md-enhance'
|
||||
import { readingTimePlugin } from '@vuepress/plugin-reading-time'
|
||||
import { seoPlugin } from '@vuepress/plugin-seo'
|
||||
import { sitemapPlugin } from '@vuepress/plugin-sitemap'
|
||||
@ -196,7 +196,7 @@ export function setupPlugins(
|
||||
alert: true,
|
||||
footnote: true,
|
||||
katex: true,
|
||||
} as MarkdownEnhanceOptions,
|
||||
} as MarkdownEnhancePluginOptions,
|
||||
options.markdownEnhance || {},
|
||||
),
|
||||
))
|
||||
|
||||
@ -5,7 +5,7 @@ import type { BaiduTongjiOptions } from '@vuepress-plume/plugin-baidu-tongji'
|
||||
import type { CopyCodeOptions } from '@vuepress-plume/plugin-copy-code'
|
||||
import type { ShikiPluginOptions } from '@vuepress-plume/plugin-shikiji'
|
||||
import type { CommentPluginOptions } from '@vuepress/plugin-comment'
|
||||
import type { MarkdownEnhanceOptions } from 'vuepress-plugin-md-enhance'
|
||||
import type { MarkdownEnhancePluginOptions } from 'vuepress-plugin-md-enhance'
|
||||
import type { ReadingTimePluginOptions } from '@vuepress/plugin-reading-time'
|
||||
import type { MarkdownPowerPluginOptions } from 'vuepress-plugin-md-power'
|
||||
|
||||
@ -54,7 +54,7 @@ export interface PlumeThemePluginOptions {
|
||||
|
||||
copyCode?: false | CopyCodeOptions
|
||||
|
||||
markdownEnhance?: false | MarkdownEnhanceOptions
|
||||
markdownEnhance?: false | MarkdownEnhancePluginOptions
|
||||
|
||||
markdownPower?: false | MarkdownPowerPluginOptions
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user