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.
@@ -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 = normalizeLink(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
- const pdfjsUrl = pdfOptions.pdfjsUrl
24
- ? `${ensureEndingSlash(withBase(pdfOptions.pdfjsUrl))}web/viewer.html`
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=${encodeURIComponent(url)}${queryString}`
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
- if (typeof pluginOptions.pdf === 'object' && pluginOptions.pdf.pdfjsUrl)
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
  }
@@ -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;
@@ -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 || 1,
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,5 @@
1
+ /**
2
+ * =|这里的文本将被黑幕隐藏,通过点击或者 hover 才可重现|=
3
+ */
4
+ import type { PluginWithOptions } from 'markdown-it';
5
+ export declare const plotPlugin: PluginWithOptions<never>;
@@ -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
+ };
@@ -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
  };
@@ -6,4 +6,5 @@ export * from './codepen.js';
6
6
  export * from './codeSandbox.js';
7
7
  export * from './replit.js';
8
8
  export * from './jsfiddle.js';
9
+ export * from './plot.js';
9
10
  export * from './plugin.js';
@@ -6,4 +6,5 @@ export * from './codepen.js';
6
6
  export * from './codeSandbox.js';
7
7
  export * from './replit.js';
8
8
  export * from './jsfiddle.js';
9
+ export * from './plot.js';
9
10
  export * from './plugin.js';
@@ -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 {};
@@ -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.51",
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.22",
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.21"
48
+ "vue": "^3.4.23"
49
49
  },
50
50
  "devDependencies": {
51
- "@iconify/json": "^2.2.200",
51
+ "@iconify/json": "^2.2.202",
52
52
  "@types/markdown-it": "^14.0.1"
53
53
  },
54
54
  "publishConfig": {