vuepress-plugin-md-power 1.0.0-rc.51 → 1.0.0-rc.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/components/Plot.vue +97 -0
- package/lib/client/composables/pdf.js +8 -10
- package/lib/client/config.js +3 -0
- package/lib/node/features/icons/plugin.js +5 -4
- package/lib/node/features/pdf.js +1 -1
- package/lib/node/features/plot.d.ts +5 -0
- package/lib/node/features/plot.js +48 -0
- package/lib/node/plugin.js +6 -0
- package/lib/shared/index.d.ts +1 -0
- package/lib/shared/index.js +1 -0
- package/lib/shared/plot.d.ts +27 -0
- package/lib/shared/plot.js +1 -0
- package/lib/shared/plugin.d.ts +2 -0
- package/package.json +4 -4
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, shallowRef } from 'vue'
|
|
3
|
+
import { onClickOutside, useMediaQuery } from '@vueuse/core'
|
|
4
|
+
import { usePageFrontmatter } from 'vuepress/client'
|
|
5
|
+
import type { PlotOptions } from '../../shared/plot.js'
|
|
6
|
+
import { pluginOptions } from '../options.js'
|
|
7
|
+
|
|
8
|
+
const props = defineProps<Omit<PlotOptions, 'tag'>>()
|
|
9
|
+
|
|
10
|
+
const matter = usePageFrontmatter()
|
|
11
|
+
|
|
12
|
+
const options = computed(() => {
|
|
13
|
+
const plot = typeof pluginOptions.plot === 'object' ? pluginOptions.plot : {}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
trigger: props.trigger || matter.value.plotTrigger || plot.trigger || 'hover',
|
|
17
|
+
color: props.color || plot.color,
|
|
18
|
+
mask: props.mask || plot.mask,
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const styles = computed(() => {
|
|
23
|
+
const plot = options.value
|
|
24
|
+
if (!plot.color && !plot.mask)
|
|
25
|
+
return {}
|
|
26
|
+
const style: Record<string, string> = {}
|
|
27
|
+
if (plot.color) {
|
|
28
|
+
if (typeof plot.color === 'string') {
|
|
29
|
+
style['--vp-c-plot-light'] = plot.color
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
style['--vp-c-plot-light'] = plot.color.light
|
|
33
|
+
style['--vp-c-plot-dark'] = plot.color.dark
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (plot.mask) {
|
|
37
|
+
if (typeof plot.mask === 'string') {
|
|
38
|
+
style['--vp-c-bg-plot-light'] = plot.mask
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
style['--vp-c-bg-plot-light'] = plot.mask.light
|
|
42
|
+
style['--vp-c-bg-plot-dark'] = plot.mask.dark
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return style
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const isMobile = useMediaQuery('(max-width: 768px)')
|
|
49
|
+
const active = ref(false)
|
|
50
|
+
const el = shallowRef<HTMLElement>()
|
|
51
|
+
|
|
52
|
+
onClickOutside(el, () => {
|
|
53
|
+
if (options.value.trigger === 'click' || isMobile.value)
|
|
54
|
+
active.value = false
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
function onClick() {
|
|
58
|
+
if (props.trigger === 'click' || isMobile.value)
|
|
59
|
+
active.value = !active.value
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<template>
|
|
64
|
+
<span
|
|
65
|
+
ref="el"
|
|
66
|
+
class="vp-plot"
|
|
67
|
+
:class="{ hover: options.trigger !== 'click', active }"
|
|
68
|
+
:style="styles"
|
|
69
|
+
@click="onClick"
|
|
70
|
+
>
|
|
71
|
+
<slot />
|
|
72
|
+
</span>
|
|
73
|
+
</template>
|
|
74
|
+
|
|
75
|
+
<style>
|
|
76
|
+
.vp-plot {
|
|
77
|
+
padding-right: 2px;
|
|
78
|
+
padding-left: 2px;
|
|
79
|
+
color: transparent;
|
|
80
|
+
background-color: var(--vp-c-bg-plot-light, #000);
|
|
81
|
+
transition: color ease 0.25s, background-color ease 0.25s;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dark .vp-plot {
|
|
85
|
+
background-color: var(--vp-c-bg-plot-dark, #fff);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.vp-plot.hover:hover,
|
|
89
|
+
.vp-plot.active {
|
|
90
|
+
color: var(--vp-c-plot-light, #fff);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.dark .vp-plot.hover:hover,
|
|
94
|
+
.dark .vp-plot.active {
|
|
95
|
+
color: var(--vp-c-plot-dark, #000);
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ensureEndingSlash } from 'vuepress/shared';
|
|
1
|
+
import { ensureEndingSlash, isLinkHttp } from 'vuepress/shared';
|
|
2
2
|
import { withBase } from 'vuepress/client';
|
|
3
|
-
import { normalizeLink } from '../utils/link.js';
|
|
4
3
|
import { pluginOptions } from '../options.js';
|
|
5
4
|
import { checkIsMobile, checkIsSafari, checkIsiPad } from '../utils/is.js';
|
|
6
5
|
function queryStringify(options) {
|
|
@@ -18,14 +17,15 @@ function queryStringify(options) {
|
|
|
18
17
|
export function renderPDF(el, url, embedType, options) {
|
|
19
18
|
if (!pluginOptions.pdf)
|
|
20
19
|
return;
|
|
21
|
-
url =
|
|
20
|
+
url = isLinkHttp(url)
|
|
21
|
+
? url
|
|
22
|
+
: new URL(withBase(url), typeof location !== 'undefined' ? location.href : '').toString();
|
|
22
23
|
const pdfOptions = pluginOptions.pdf === true ? {} : pluginOptions.pdf;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
: '';
|
|
24
|
+
pdfOptions.pdfjsUrl ??= 'https://static.pengzhanbo.cn/pdfjs/';
|
|
25
|
+
const pdfjsUrl = `${ensureEndingSlash(withBase(pdfOptions.pdfjsUrl))}web/viewer.html`;
|
|
26
26
|
const queryString = queryStringify(options);
|
|
27
27
|
const source = embedType === 'pdfjs'
|
|
28
|
-
? `${pdfjsUrl}?file=${
|
|
28
|
+
? `${pdfjsUrl}?file=${url}${queryString}`
|
|
29
29
|
: `${url}${queryString}`;
|
|
30
30
|
const tagName = embedType === 'pdfjs' || embedType === 'iframe'
|
|
31
31
|
? 'iframe'
|
|
@@ -69,7 +69,5 @@ export function usePDF(el, url, options) {
|
|
|
69
69
|
const embedType = isSafariDesktop ? 'iframe' : 'embed';
|
|
70
70
|
return renderPDF(el, url, embedType, options);
|
|
71
71
|
}
|
|
72
|
-
|
|
73
|
-
return renderPDF(el, url, 'pdfjs', options);
|
|
74
|
-
el.innerHTML = `<p>This browser does not support embedding PDFs. Please download the PDF to view it: <a href='${url}' target='_blank'>Download PDF</a></p>`;
|
|
72
|
+
return renderPDF(el, url, 'pdfjs', options);
|
|
75
73
|
}
|
package/lib/client/config.js
CHANGED
|
@@ -6,6 +6,7 @@ import Bilibili from './components/Bilibili.vue';
|
|
|
6
6
|
import Youtube from './components/Youtube.vue';
|
|
7
7
|
import Replit from './components/Replit.vue';
|
|
8
8
|
import CodeSandbox from './components/CodeSandbox.vue';
|
|
9
|
+
import Plot from './components/Plot.vue';
|
|
9
10
|
import '@internal/md-power/icons.css';
|
|
10
11
|
export default defineClientConfig({
|
|
11
12
|
enhance({ router, app }) {
|
|
@@ -19,6 +20,8 @@ export default defineClientConfig({
|
|
|
19
20
|
app.component('ReplitViewer', Replit);
|
|
20
21
|
if (pluginOptions.codeSandbox)
|
|
21
22
|
app.component('CodeSandboxViewer', CodeSandbox);
|
|
23
|
+
if (pluginOptions.plot)
|
|
24
|
+
app.component('Plot', Plot);
|
|
22
25
|
if (__VUEPRESS_SSR__)
|
|
23
26
|
return;
|
|
24
27
|
if (pluginOptions.caniuse) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { parseRect } from '../../utils/parseRect.js';
|
|
2
|
+
const [openTag, endTag] = [':[', ']:'];
|
|
2
3
|
function createTokenizer(addIcon) {
|
|
3
4
|
return (state, silent) => {
|
|
4
5
|
let found = false;
|
|
5
6
|
const max = state.posMax;
|
|
6
7
|
const start = state.pos;
|
|
7
|
-
if (state.src.slice(start, start + 2) !==
|
|
8
|
+
if (state.src.slice(start, start + 2) !== openTag)
|
|
8
9
|
return false;
|
|
9
10
|
if (silent)
|
|
10
11
|
return false;
|
|
@@ -13,7 +14,7 @@ function createTokenizer(addIcon) {
|
|
|
13
14
|
return false;
|
|
14
15
|
state.pos = start + 2;
|
|
15
16
|
while (state.pos < max) {
|
|
16
|
-
if (state.src.slice(state.pos, state.pos + 2) ===
|
|
17
|
+
if (state.src.slice(state.pos, state.pos + 2) === endTag) {
|
|
17
18
|
found = true;
|
|
18
19
|
break;
|
|
19
20
|
}
|
|
@@ -35,7 +36,7 @@ function createTokenizer(addIcon) {
|
|
|
35
36
|
const [iconName, options = ''] = content.split(/\s+/);
|
|
36
37
|
const [size, color] = options.split('/');
|
|
37
38
|
const open = state.push('iconify_open', 'span', 1);
|
|
38
|
-
open.markup =
|
|
39
|
+
open.markup = openTag;
|
|
39
40
|
const className = addIcon(iconName);
|
|
40
41
|
if (className)
|
|
41
42
|
open.attrSet('class', className);
|
|
@@ -49,7 +50,7 @@ function createTokenizer(addIcon) {
|
|
|
49
50
|
const text = state.push('text', '', 0);
|
|
50
51
|
text.content = className ? '' : iconName;
|
|
51
52
|
const close = state.push('iconify_close', 'span', -1);
|
|
52
|
-
close.markup =
|
|
53
|
+
close.markup = endTag;
|
|
53
54
|
state.pos = state.posMax + 2;
|
|
54
55
|
state.posMax = max;
|
|
55
56
|
return true;
|
package/lib/node/features/pdf.js
CHANGED
|
@@ -37,7 +37,7 @@ function createPDFRuleBlock() {
|
|
|
37
37
|
src,
|
|
38
38
|
page: +page || 1,
|
|
39
39
|
noToolbar: Boolean(attrs.noToolbar ?? false),
|
|
40
|
-
zoom: +attrs.zoom ||
|
|
40
|
+
zoom: +attrs.zoom || 50,
|
|
41
41
|
width: attrs.width ? parseRect(attrs.width) : '100%',
|
|
42
42
|
height: attrs.height ? parseRect(attrs.height) : '',
|
|
43
43
|
ratio: attrs.ratio ? parseRect(attrs.ratio) : '',
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const [openTag, endTag] = ['=|', '|='];
|
|
2
|
+
function createTokenizer() {
|
|
3
|
+
return (state, silent) => {
|
|
4
|
+
let found = false;
|
|
5
|
+
const max = state.posMax;
|
|
6
|
+
const start = state.pos;
|
|
7
|
+
if (state.src.slice(start, start + 2) !== openTag)
|
|
8
|
+
return false;
|
|
9
|
+
if (silent)
|
|
10
|
+
return false;
|
|
11
|
+
// =||=
|
|
12
|
+
if (max - start < 5)
|
|
13
|
+
return false;
|
|
14
|
+
state.pos = start + 2;
|
|
15
|
+
while (state.pos < max) {
|
|
16
|
+
if (state.src.slice(state.pos - 1, state.pos + 1) === endTag) {
|
|
17
|
+
found = true;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
state.md.inline.skipToken(state);
|
|
21
|
+
}
|
|
22
|
+
if (!found || start + 2 === state.pos) {
|
|
23
|
+
state.pos = start;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const content = state.src.slice(start + 2, state.pos - 1);
|
|
27
|
+
// 不允许前后带有空格
|
|
28
|
+
if (/^\s|\s$/.test(content)) {
|
|
29
|
+
state.pos = start;
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
// found!
|
|
33
|
+
state.posMax = state.pos - 1;
|
|
34
|
+
state.pos = start + 2;
|
|
35
|
+
const open = state.push('plot_open', 'Plot', 1);
|
|
36
|
+
open.markup = openTag;
|
|
37
|
+
const text = state.push('text', '', 0);
|
|
38
|
+
text.content = content;
|
|
39
|
+
const close = state.push('plot_close', 'Plot', -1);
|
|
40
|
+
close.markup = endTag;
|
|
41
|
+
state.pos = state.posMax + 2;
|
|
42
|
+
state.posMax = max;
|
|
43
|
+
return true;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export const plotPlugin = (md) => {
|
|
47
|
+
md.inline.ruler.before('emphasis', 'plot', createTokenizer());
|
|
48
|
+
};
|
package/lib/node/plugin.js
CHANGED
|
@@ -8,6 +8,7 @@ import { codepenPlugin } from './features/codepen.js';
|
|
|
8
8
|
import { replitPlugin } from './features/replit.js';
|
|
9
9
|
import { codeSandboxPlugin } from './features/codeSandbox.js';
|
|
10
10
|
import { jsfiddlePlugin } from './features/jsfiddle.js';
|
|
11
|
+
import { plotPlugin } from './features/plot.js';
|
|
11
12
|
const __dirname = getDirname(import.meta.url);
|
|
12
13
|
export function markdownPowerPlugin(options = {}) {
|
|
13
14
|
return (app) => {
|
|
@@ -59,6 +60,11 @@ export function markdownPowerPlugin(options = {}) {
|
|
|
59
60
|
// @[jsfiddle](user/id)
|
|
60
61
|
md.use(jsfiddlePlugin);
|
|
61
62
|
}
|
|
63
|
+
if (options.plot === true
|
|
64
|
+
|| (typeof options.plot === 'object' && options.plot.tag !== false)) {
|
|
65
|
+
// =|plot|=
|
|
66
|
+
md.use(plotPlugin);
|
|
67
|
+
}
|
|
62
68
|
},
|
|
63
69
|
};
|
|
64
70
|
};
|
package/lib/shared/index.d.ts
CHANGED
package/lib/shared/index.js
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface PlotOptions {
|
|
2
|
+
/**
|
|
3
|
+
* 是否启用 `=| |=` markdown (该标记为非标准标记,脱离插件将不生效)
|
|
4
|
+
* @default true
|
|
5
|
+
*/
|
|
6
|
+
tag?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* 遮罩层颜色
|
|
9
|
+
*/
|
|
10
|
+
mask?: string | {
|
|
11
|
+
light: string;
|
|
12
|
+
dark: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* 文本颜色
|
|
16
|
+
*/
|
|
17
|
+
color?: string | {
|
|
18
|
+
light: string;
|
|
19
|
+
dark: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* 触发方式
|
|
23
|
+
*
|
|
24
|
+
* @default 'hover'
|
|
25
|
+
*/
|
|
26
|
+
trigger?: 'hover' | 'click';
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/shared/plugin.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { CanIUseOptions } from './caniuse.js';
|
|
2
2
|
import type { PDFOptions } from './pdf.js';
|
|
3
3
|
import type { IconsOptions } from './icons.js';
|
|
4
|
+
import type { PlotOptions } from './plot.js';
|
|
4
5
|
export interface MarkdownPowerPluginOptions {
|
|
5
6
|
pdf?: boolean | PDFOptions;
|
|
6
7
|
icons?: boolean | IconsOptions;
|
|
8
|
+
plot?: boolean | PlotOptions;
|
|
7
9
|
bilibili?: boolean;
|
|
8
10
|
youtube?: boolean;
|
|
9
11
|
codepen?: boolean;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vuepress-plugin-md-power",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.53",
|
|
5
5
|
"description": "The Plugin for VuePres 2 - markdown power",
|
|
6
6
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -40,15 +40,15 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@iconify/utils": "^2.1.
|
|
43
|
+
"@iconify/utils": "^2.1.23",
|
|
44
44
|
"@vueuse/core": "^10.9.0",
|
|
45
45
|
"local-pkg": "^0.5.0",
|
|
46
46
|
"markdown-it-container": "^4.0.0",
|
|
47
47
|
"nanoid": "^5.0.7",
|
|
48
|
-
"vue": "^3.4.
|
|
48
|
+
"vue": "^3.4.23"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@iconify/json": "^2.2.
|
|
51
|
+
"@iconify/json": "^2.2.202",
|
|
52
52
|
"@types/markdown-it": "^14.0.1"
|
|
53
53
|
},
|
|
54
54
|
"publishConfig": {
|