feat(plugin-md-power): add <Plot /> and =||= syntax

This commit is contained in:
pengzhanbo 2024-04-21 00:14:21 +08:00
parent 7daf764fa0
commit 9a5eda49a8
9 changed files with 212 additions and 5 deletions

View File

@ -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,

View 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>

View 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())
}

View File

@ -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'

View 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'
}

View File

@ -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

View File

@ -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);
}

View File

@ -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 || {},
),
))

View File

@ -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