vuepress-plugin-md-power 1.0.0-rc.180 → 1.0.0-rc.182
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,228 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { QRCodeToDataURLOptions, QRCodeToStringOptions } from 'qrcode'
|
|
3
|
+
import type { QRCodeProps } from '../../shared/index.js'
|
|
4
|
+
import { isLinkWithProtocol } from '@vuepress/helper/client'
|
|
5
|
+
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
6
|
+
import { resolveRoute, usePage, withBase } from 'vuepress/client'
|
|
7
|
+
|
|
8
|
+
const { title, text, mode, align = 'left', reverse = false, svg = false, width, level, version, mask, margin = 2, scale = 4, light, dark } = defineProps<QRCodeProps>()
|
|
9
|
+
|
|
10
|
+
const page = usePage()
|
|
11
|
+
|
|
12
|
+
let qr: typeof import('qrcode') | null = null
|
|
13
|
+
|
|
14
|
+
const qrcode = ref('')
|
|
15
|
+
const parsedText = ref('')
|
|
16
|
+
const isLink = ref(false)
|
|
17
|
+
|
|
18
|
+
const styles = computed(() => {
|
|
19
|
+
const size = typeof width === 'number' ? width : width ? Number.parseInt(width) : undefined
|
|
20
|
+
return size ? { '--vp-qrcode-size': `${size}px` } : undefined
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function parseText(): string | void {
|
|
24
|
+
isLink.value = false
|
|
25
|
+
if (!text || __VUEPRESS_SSR__)
|
|
26
|
+
return ''
|
|
27
|
+
|
|
28
|
+
if (text === '.') {
|
|
29
|
+
isLink.value = true
|
|
30
|
+
return location.href.split(/[?#]/)[0]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (isLinkWithProtocol(text)) {
|
|
34
|
+
isLink.value = true
|
|
35
|
+
return text.startsWith('//') ? `${location.protocol}${text}` : text
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (text.startsWith('/') || text.startsWith('./')) {
|
|
39
|
+
const [routePath, ...rest] = text.split(/([?#])/)
|
|
40
|
+
const currentPath = page.value.filePathRelative ? `/${page.value.filePathRelative}` : undefined
|
|
41
|
+
const { notFound, path } = resolveRoute(routePath, currentPath)
|
|
42
|
+
if (notFound) {
|
|
43
|
+
return text
|
|
44
|
+
}
|
|
45
|
+
isLink.value = true
|
|
46
|
+
return new URL(`${withBase(path)}${rest.join('')}`, location.href).toString()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return text
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onMounted(async () => {
|
|
53
|
+
const callback = (_: any, url: string) => qrcode.value = url
|
|
54
|
+
|
|
55
|
+
watch(
|
|
56
|
+
() => [text, svg, level, version, mask, margin, scale, light, dark],
|
|
57
|
+
async () => {
|
|
58
|
+
const text = parseText()
|
|
59
|
+
parsedText.value = text || ''
|
|
60
|
+
if (!text) {
|
|
61
|
+
qrcode.value = ''
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
qr ??= (await import(/* webpackChunkName: "qrcode" */ 'qrcode')).default
|
|
66
|
+
const opts: QRCodeToDataURLOptions & QRCodeToStringOptions = {
|
|
67
|
+
version,
|
|
68
|
+
maskPattern: mask,
|
|
69
|
+
errorCorrectionLevel: (level ? level.toUpperCase() : 'M') as any,
|
|
70
|
+
width: 300 * Math.round(window.devicePixelRatio || 1),
|
|
71
|
+
margin,
|
|
72
|
+
scale,
|
|
73
|
+
color: { dark, light },
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (svg)
|
|
77
|
+
qr.toString(text, { type: 'svg', ...opts }, callback)
|
|
78
|
+
else
|
|
79
|
+
qr.toDataURL(text, { type: 'image/png', ...opts }, callback)
|
|
80
|
+
},
|
|
81
|
+
{ immediate: true },
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
onUnmounted(() => {
|
|
86
|
+
qr = null
|
|
87
|
+
})
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
<template>
|
|
91
|
+
<div v-if="qrcode" class="vp-qrcode" :class="{ card: mode === 'card', reverse, [align]: true }">
|
|
92
|
+
<div class="qrcode-content">
|
|
93
|
+
<div v-if="svg" class="qrcode-svg" :style="styles" :title="parsedText" v-html="qrcode" />
|
|
94
|
+
<img v-else class="qrcode-img" :src="qrcode" :alt="parsedText" :title="parsedText" :style="styles">
|
|
95
|
+
<div v-if="title && mode !== 'card'" class="qrcode-label">
|
|
96
|
+
{{ title }}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<div v-if="mode === 'card'" class="qrcode-info">
|
|
101
|
+
<p v-if="title" class="qrcode-title">
|
|
102
|
+
{{ title }}
|
|
103
|
+
</p>
|
|
104
|
+
<p v-if="parsedText">
|
|
105
|
+
<a v-if="isLink" :href="parsedText" rel="noopener noreferrer" target="_blank">
|
|
106
|
+
{{ parsedText }}
|
|
107
|
+
</a>
|
|
108
|
+
<span v-else v-html="parsedText.replaceAll('\n', '<br>')" />
|
|
109
|
+
</p>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</template>
|
|
113
|
+
|
|
114
|
+
<style scoped>
|
|
115
|
+
.vp-qrcode {
|
|
116
|
+
--vp-qrcode-size: 128px;
|
|
117
|
+
|
|
118
|
+
margin: 16px 0;
|
|
119
|
+
overflow: hidden;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@media (min-width: 768px) {
|
|
123
|
+
.vp-qrcode {
|
|
124
|
+
--vp-qrcode-size: 150px;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.vp-qrcode:not(.card).center {
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
justify-content: center;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.vp-qrcode:not(.card).right {
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: flex-end;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.vp-qrcode .qrcode-content {
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
gap: 8px;
|
|
144
|
+
align-items: center;
|
|
145
|
+
justify-content: center;
|
|
146
|
+
width: max-content;
|
|
147
|
+
max-width: 100%;
|
|
148
|
+
overflow: hidden;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.vp-qrcode .qrcode-label {
|
|
152
|
+
font-size: 14px;
|
|
153
|
+
color: var(--vp-c-text-2);
|
|
154
|
+
text-align: center;
|
|
155
|
+
word-break: break-all;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.vp-qrcode.card {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
gap: 8px;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
padding: 16px 20px;
|
|
165
|
+
background-color: var(--vp-c-bg-soft);
|
|
166
|
+
border-radius: 8px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.vp-qrcode .qrcode-svg,
|
|
170
|
+
.vp-qrcode .qrcode-img {
|
|
171
|
+
width: var(--vp-qrcode-size);
|
|
172
|
+
max-width: 100%;
|
|
173
|
+
height: var(--vp-qrcode-size);
|
|
174
|
+
aspect-ratio: 1/1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.vp-qrcode .qrcode-svg :deep(svg) {
|
|
178
|
+
max-width: 100%;
|
|
179
|
+
max-height: 100%;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.vp-qrcode .qrcode-info {
|
|
183
|
+
display: flex;
|
|
184
|
+
flex: 1;
|
|
185
|
+
flex-direction: column;
|
|
186
|
+
min-width: 0;
|
|
187
|
+
text-align: center;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.vp-qrcode .qrcode-info > p {
|
|
191
|
+
margin: 0;
|
|
192
|
+
word-break: break-all;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.vp-qrcode .qrcode-info .qrcode-title {
|
|
196
|
+
font-weight: 600;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@media (min-width: 960px) {
|
|
200
|
+
.vp-qrcode.card {
|
|
201
|
+
flex-direction: row;
|
|
202
|
+
gap: 16px;
|
|
203
|
+
align-items: flex-start;
|
|
204
|
+
justify-content: flex-start;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.vp-qrcode.card .qrcode-info {
|
|
208
|
+
align-self: center;
|
|
209
|
+
text-align: left;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.vp-qrcode.card:where(.reverse, .right) {
|
|
213
|
+
flex-direction: row-reverse;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.vp-qrcode.card:where(.reverse, .right) .qrcode-info {
|
|
217
|
+
text-align: right;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.vp-qrcode.card.center {
|
|
221
|
+
align-items: center;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.vp-qrcode.card.center .qrcode-info {
|
|
225
|
+
flex: initial;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
</style>
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { onContentUpdated, useRouter } from "vuepress/client";
|
|
1
|
+
import { onContentUpdated } from "vuepress/client";
|
|
3
2
|
|
|
4
3
|
//#region src/client/composables/mark.ts
|
|
5
4
|
const MARK_MODE_ATTR = "data-mark-mode";
|
|
6
5
|
const MARK_MODE_LAZY = "lazy";
|
|
7
6
|
const MARK_VISIBLE_CLASS = "vp-mark-visible";
|
|
8
7
|
const MARK_BOUND_ATTR = "data-vp-mark-bound";
|
|
9
|
-
const MARK_SELECTOR = "
|
|
10
|
-
const DOC_SELECTOR = ".vp-doc";
|
|
8
|
+
const MARK_SELECTOR = "mark";
|
|
11
9
|
const BOUND_SELECTOR = `${MARK_SELECTOR}[${MARK_BOUND_ATTR}="1"]`;
|
|
12
10
|
function setupMarkHighlight(mode) {
|
|
13
11
|
if (typeof window === "undefined" || __VUEPRESS_SSR__) return;
|
|
@@ -18,9 +16,7 @@ function setupMarkHighlight(mode) {
|
|
|
18
16
|
}
|
|
19
17
|
root.setAttribute(MARK_MODE_ATTR, MARK_MODE_LAZY);
|
|
20
18
|
let intersectionObserver = null;
|
|
21
|
-
let mutationObserver = null;
|
|
22
19
|
let rafId = null;
|
|
23
|
-
let removeAfterEach = null;
|
|
24
20
|
const ensureObserver = () => {
|
|
25
21
|
if (!intersectionObserver) intersectionObserver = new IntersectionObserver((entries, obs) => {
|
|
26
22
|
for (const entry of entries) {
|
|
@@ -57,51 +53,18 @@ function setupMarkHighlight(mode) {
|
|
|
57
53
|
bindMarks();
|
|
58
54
|
});
|
|
59
55
|
};
|
|
60
|
-
const observeDocMutations = () => {
|
|
61
|
-
const doc = document.querySelector(DOC_SELECTOR);
|
|
62
|
-
if (!doc) return;
|
|
63
|
-
if (mutationObserver) mutationObserver.disconnect();
|
|
64
|
-
mutationObserver = new MutationObserver((mutations) => {
|
|
65
|
-
if (mutations.some((mutation) => mutation.addedNodes.length > 0)) scheduleBind();
|
|
66
|
-
});
|
|
67
|
-
mutationObserver.observe(doc, {
|
|
68
|
-
childList: true,
|
|
69
|
-
subtree: true
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
56
|
const resetObserver = () => {
|
|
73
|
-
|
|
57
|
+
if (!intersectionObserver) return;
|
|
58
|
+
intersectionObserver.disconnect();
|
|
59
|
+
intersectionObserver = null;
|
|
60
|
+
Array.from(document.querySelectorAll(BOUND_SELECTOR) || []).forEach((mark) => {
|
|
74
61
|
if (!mark.classList.contains(MARK_VISIBLE_CLASS)) mark.removeAttribute(MARK_BOUND_ATTR);
|
|
75
62
|
});
|
|
76
|
-
if (intersectionObserver) {
|
|
77
|
-
intersectionObserver.disconnect();
|
|
78
|
-
intersectionObserver = null;
|
|
79
|
-
}
|
|
80
63
|
};
|
|
81
|
-
const router = useRouter();
|
|
82
|
-
onMounted(() => {
|
|
83
|
-
observeDocMutations();
|
|
84
|
-
scheduleBind();
|
|
85
|
-
});
|
|
86
64
|
onContentUpdated(() => {
|
|
87
65
|
resetObserver();
|
|
88
|
-
observeDocMutations();
|
|
89
66
|
scheduleBind();
|
|
90
67
|
});
|
|
91
|
-
if (router?.afterEach) removeAfterEach = router.afterEach(() => {
|
|
92
|
-
resetObserver();
|
|
93
|
-
observeDocMutations();
|
|
94
|
-
scheduleBind();
|
|
95
|
-
});
|
|
96
|
-
if (router?.isReady) router.isReady().then(() => scheduleBind()).catch(() => {});
|
|
97
|
-
onBeforeUnmount(() => {
|
|
98
|
-
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
99
|
-
resetObserver();
|
|
100
|
-
mutationObserver?.disconnect();
|
|
101
|
-
mutationObserver = null;
|
|
102
|
-
removeAfterEach?.();
|
|
103
|
-
removeAfterEach = null;
|
|
104
|
-
});
|
|
105
68
|
}
|
|
106
69
|
|
|
107
70
|
//#endregion
|
package/lib/node/index.d.ts
CHANGED
|
@@ -501,6 +501,12 @@ interface MarkdownPowerPluginOptions {
|
|
|
501
501
|
* @default false
|
|
502
502
|
*/
|
|
503
503
|
table?: boolean | TableContainerOptions;
|
|
504
|
+
/**
|
|
505
|
+
* 是否启用 二维码 嵌入语法
|
|
506
|
+
*
|
|
507
|
+
* @default false
|
|
508
|
+
*/
|
|
509
|
+
qrcode?: boolean;
|
|
504
510
|
/**
|
|
505
511
|
* 是否启用 自动填充 图片宽高属性
|
|
506
512
|
*
|
|
@@ -521,6 +527,89 @@ interface MarkdownPowerPluginOptions {
|
|
|
521
527
|
imageSize?: boolean | 'local' | 'all';
|
|
522
528
|
}
|
|
523
529
|
//#endregion
|
|
530
|
+
//#region src/shared/qrcode.d.ts
|
|
531
|
+
interface QRCodeMeta extends QRCodeProps {
|
|
532
|
+
/**
|
|
533
|
+
* mode: 'card' 的别名
|
|
534
|
+
*/
|
|
535
|
+
card?: boolean;
|
|
536
|
+
}
|
|
537
|
+
interface QRCodeProps {
|
|
538
|
+
/**
|
|
539
|
+
* 二维码标题
|
|
540
|
+
* 作为 HTML 标签的 `title` 属性、`alt` 属性
|
|
541
|
+
*/
|
|
542
|
+
title?: string;
|
|
543
|
+
/**
|
|
544
|
+
* 二维码内容
|
|
545
|
+
*/
|
|
546
|
+
text?: string;
|
|
547
|
+
/**
|
|
548
|
+
* 二维码宽度
|
|
549
|
+
*/
|
|
550
|
+
width?: number | string;
|
|
551
|
+
/**
|
|
552
|
+
* 显示模式
|
|
553
|
+
* - img: 以图片的形式显示二维码
|
|
554
|
+
* - card: 以卡片的形式显示,卡片以左右布局,左侧二维码,右侧 标题 + 内容
|
|
555
|
+
* @default 'img'
|
|
556
|
+
*/
|
|
557
|
+
mode?: 'img' | 'card';
|
|
558
|
+
/**
|
|
559
|
+
* 在 card 模式下是否翻转布局
|
|
560
|
+
*/
|
|
561
|
+
reverse?: boolean;
|
|
562
|
+
/**
|
|
563
|
+
* 二维码的对齐方式
|
|
564
|
+
* @default 'left'
|
|
565
|
+
*/
|
|
566
|
+
align?: 'left' | 'center' | 'right';
|
|
567
|
+
/**
|
|
568
|
+
* 是否渲染为 SVG 格式的二维码
|
|
569
|
+
* 默认输出为 PNG 格式的 dataURL
|
|
570
|
+
* @default false
|
|
571
|
+
*/
|
|
572
|
+
svg?: boolean;
|
|
573
|
+
/**
|
|
574
|
+
* 纠错等级。
|
|
575
|
+
* 可能的取值为低、中、四分位、高,分别对应 L、M、Q、H。
|
|
576
|
+
* @default 'M'
|
|
577
|
+
*/
|
|
578
|
+
level?: 'L' | 'M' | 'Q' | 'H' | 'l' | 'm' | 'q' | 'h';
|
|
579
|
+
/**
|
|
580
|
+
* 二维码版本。若未指定,将自动计算更合适的值。
|
|
581
|
+
* 取值范围 1-40
|
|
582
|
+
*/
|
|
583
|
+
version?: number;
|
|
584
|
+
/**
|
|
585
|
+
* 用于遮蔽符号的掩码模式。
|
|
586
|
+
* 可能的取值为0、1、2、3、4、5、6、7。
|
|
587
|
+
* 若未指定,系统将自动计算更合适的值。
|
|
588
|
+
*/
|
|
589
|
+
mask?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
590
|
+
/**
|
|
591
|
+
* 定义静区应有多宽。
|
|
592
|
+
* @default 4
|
|
593
|
+
*/
|
|
594
|
+
margin?: number;
|
|
595
|
+
/**
|
|
596
|
+
* 缩放因子。值为1表示每个模块(黑点)对应1像素。
|
|
597
|
+
*/
|
|
598
|
+
scale?: number;
|
|
599
|
+
/**
|
|
600
|
+
* 暗色模块的颜色。值必须为十六进制格式(RGBA)。
|
|
601
|
+
* 注意:暗色应始终比浅色模块的颜色更深。
|
|
602
|
+
* @default '#000000ff'
|
|
603
|
+
*/
|
|
604
|
+
light?: string;
|
|
605
|
+
/**
|
|
606
|
+
* 亮色模块的颜色。值必须为十六进制格式(RGBA)。
|
|
607
|
+
* 注意:亮色应始终比暗色模块的颜色更浅。
|
|
608
|
+
* @default '#ffffffff'
|
|
609
|
+
*/
|
|
610
|
+
dark?: string;
|
|
611
|
+
}
|
|
612
|
+
//#endregion
|
|
524
613
|
//#region src/shared/replit.d.ts
|
|
525
614
|
interface ReplitTokenMeta extends SizeOptions {
|
|
526
615
|
title?: string;
|
|
@@ -578,4 +667,4 @@ declare function resolveImageSize(app: App, url: string, remote?: boolean): Prom
|
|
|
578
667
|
//#region src/node/plugin.d.ts
|
|
579
668
|
declare function markdownPowerPlugin(options?: MarkdownPowerPluginOptions): Plugin;
|
|
580
669
|
//#endregion
|
|
581
|
-
export { AcFunTokenMeta, ArtPlayerTokenMeta, BilibiliTokenMeta, CanIUseMode, CanIUseOptions, CanIUseTokenMeta, CodeSandboxTokenMeta, CodeTabsOptions, CodepenTokenMeta, DemoContainerRender, DemoFile, DemoMeta, FileTreeIconMode, FileTreeOptions, FontAwesomeAssetBuiltIn, FontAwesomePrefix, FontAwesomeProvider, IconAssetLink, IconFontProvider, IconOptions, IconProviderBase, IconifyPrefix, IconifyProvider, JSFiddleTokenMeta, LiteralUnion, MarkdownDemoEnv, MarkdownPowerPluginOptions, NpmToOptions, NpmToPackageManager, PDFEmbedType, PDFOptions, PDFTokenMeta, PlotOptions, ReplEditorData, ReplOptions, ReplitTokenMeta, SizeOptions, ThemeOptions, VideoOptions, YoutubeTokenMeta, createCodeTabIconGetter, markdownPowerPlugin, resolveImageSize };
|
|
670
|
+
export { AcFunTokenMeta, ArtPlayerTokenMeta, BilibiliTokenMeta, CanIUseMode, CanIUseOptions, CanIUseTokenMeta, CodeSandboxTokenMeta, CodeTabsOptions, CodepenTokenMeta, DemoContainerRender, DemoFile, DemoMeta, FileTreeIconMode, FileTreeOptions, FontAwesomeAssetBuiltIn, FontAwesomePrefix, FontAwesomeProvider, IconAssetLink, IconFontProvider, IconOptions, IconProviderBase, IconifyPrefix, IconifyProvider, JSFiddleTokenMeta, LiteralUnion, MarkdownDemoEnv, MarkdownPowerPluginOptions, NpmToOptions, NpmToPackageManager, PDFEmbedType, PDFOptions, PDFTokenMeta, PlotOptions, QRCodeMeta, QRCodeProps, ReplEditorData, ReplOptions, ReplitTokenMeta, SizeOptions, ThemeOptions, VideoOptions, YoutubeTokenMeta, createCodeTabIconGetter, markdownPowerPlugin, resolveImageSize };
|
package/lib/node/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { mark } from "@mdit/plugin-mark";
|
|
|
24
24
|
import { sub } from "@mdit/plugin-sub";
|
|
25
25
|
import { sup } from "@mdit/plugin-sup";
|
|
26
26
|
import { tasklist } from "@mdit/plugin-tasklist";
|
|
27
|
+
import cjsFriendly from "markdown-it-cjk-friendly";
|
|
27
28
|
|
|
28
29
|
//#region src/node/fileIcons/definitions.ts
|
|
29
30
|
const defaultFolder = "vscode-icons:default-folder";
|
|
@@ -1776,11 +1777,12 @@ function parseFileTreeRawContent(content) {
|
|
|
1776
1777
|
children: []
|
|
1777
1778
|
};
|
|
1778
1779
|
const stack = [root];
|
|
1779
|
-
const lines = content.
|
|
1780
|
+
const lines = content.trimEnd().split("\n");
|
|
1781
|
+
const spaceLength = lines[0].match(/^\s*/)?.[0].length ?? 0;
|
|
1780
1782
|
for (const line of lines) {
|
|
1781
1783
|
const match = line.match(/^(\s*)-(.*)$/);
|
|
1782
1784
|
if (!match) continue;
|
|
1783
|
-
const level = Math.floor(match[1].length / 2);
|
|
1785
|
+
const level = Math.floor((match[1].length - spaceLength) / 2);
|
|
1784
1786
|
const info = match[2].trim();
|
|
1785
1787
|
while (stack.length > 0 && stack[stack.length - 1].level >= level) stack.pop();
|
|
1786
1788
|
const parent = stack[stack.length - 1];
|
|
@@ -3496,6 +3498,36 @@ const pdfPlugin = (md) => {
|
|
|
3496
3498
|
});
|
|
3497
3499
|
};
|
|
3498
3500
|
|
|
3501
|
+
//#endregion
|
|
3502
|
+
//#region src/node/embed/qrcode.ts
|
|
3503
|
+
const qrcodePlugin = (md) => {
|
|
3504
|
+
createEmbedRuleBlock(md, {
|
|
3505
|
+
type: "qrcode",
|
|
3506
|
+
syntaxPattern: /^@\[qrcode([^\]]*)\]\(([^)]*)\)/,
|
|
3507
|
+
meta([, info, text]) {
|
|
3508
|
+
const { attrs: attrs$1 } = resolveAttrs(info);
|
|
3509
|
+
const { card, ...rest } = omit(attrs$1, ["text"]);
|
|
3510
|
+
return {
|
|
3511
|
+
text,
|
|
3512
|
+
...rest,
|
|
3513
|
+
mode: rest.mode || (card ? "card" : "img")
|
|
3514
|
+
};
|
|
3515
|
+
},
|
|
3516
|
+
content(meta) {
|
|
3517
|
+
return `<VPQRCode${stringifyAttrs(meta)} />`;
|
|
3518
|
+
}
|
|
3519
|
+
});
|
|
3520
|
+
createContainerSyntaxPlugin(md, "qrcode", (tokens, index) => {
|
|
3521
|
+
const { content, meta } = tokens[index];
|
|
3522
|
+
const { card, ...rest } = omit(meta, ["text"]);
|
|
3523
|
+
return `<VPQRCode ${stringifyAttrs({
|
|
3524
|
+
text: content?.trim(),
|
|
3525
|
+
...rest,
|
|
3526
|
+
mode: rest.mode || (card ? "card" : "img")
|
|
3527
|
+
})} />`;
|
|
3528
|
+
});
|
|
3529
|
+
};
|
|
3530
|
+
|
|
3499
3531
|
//#endregion
|
|
3500
3532
|
//#region src/node/embed/video/acfun.ts
|
|
3501
3533
|
const AC_FUN_LINK = "https://www.acfun.cn/player";
|
|
@@ -3720,6 +3752,7 @@ function embedSyntaxPlugin(md, options) {
|
|
|
3720
3752
|
if (options.replit) md.use(replitPlugin);
|
|
3721
3753
|
if (options.codeSandbox) md.use(codeSandboxPlugin);
|
|
3722
3754
|
if (options.jsfiddle) md.use(jsfiddlePlugin);
|
|
3755
|
+
if (options.qrcode) md.use(qrcodePlugin);
|
|
3723
3756
|
}
|
|
3724
3757
|
|
|
3725
3758
|
//#endregion
|
|
@@ -4209,6 +4242,7 @@ const plotPlugin = (md) => {
|
|
|
4209
4242
|
//#endregion
|
|
4210
4243
|
//#region src/node/inline/index.ts
|
|
4211
4244
|
function inlineSyntaxPlugin(md, options) {
|
|
4245
|
+
md.use(cjsFriendly);
|
|
4212
4246
|
md.use(attrs);
|
|
4213
4247
|
md.use(mark);
|
|
4214
4248
|
md.use(sub);
|
|
@@ -4331,13 +4365,16 @@ async function prepareConfigFile(app, options) {
|
|
|
4331
4365
|
imports.add(`import VPTable from '${CLIENT_FOLDER}components/VPTable.vue'`);
|
|
4332
4366
|
enhances.add(`app.component('VPTable', VPTable)`);
|
|
4333
4367
|
}
|
|
4368
|
+
if (options.qrcode) {
|
|
4369
|
+
imports.add(`import VPQRCode from '${CLIENT_FOLDER}components/VPQRCode.vue'`);
|
|
4370
|
+
enhances.add(`app.component('VPQRCode', VPQRCode)`);
|
|
4371
|
+
}
|
|
4334
4372
|
const setupIcon = prepareIcon(imports, options.icon);
|
|
4335
4373
|
const setupStmts = [];
|
|
4336
4374
|
const iconSetup = setupIcon.trim();
|
|
4337
4375
|
if (iconSetup) setupStmts.push(iconSetup);
|
|
4338
|
-
const markMode = options.mark === "lazy" ? "lazy" : "eager";
|
|
4339
4376
|
imports.add(`import { setupMarkHighlight } from '${CLIENT_FOLDER}composables/mark.js'`);
|
|
4340
|
-
setupStmts.push(`setupMarkHighlight(${JSON.stringify(
|
|
4377
|
+
setupStmts.push(`setupMarkHighlight(${JSON.stringify(options.mark === "lazy" ? "lazy" : "eager")})`);
|
|
4341
4378
|
const setupContent = setupStmts.length ? ` ${setupStmts.join("\n ")}\n` : "";
|
|
4342
4379
|
return app.writeTemp("md-power/config.js", `\
|
|
4343
4380
|
import { defineClientConfig } from 'vuepress/client'
|
|
@@ -4396,6 +4433,7 @@ function markdownPowerPlugin(options = {}) {
|
|
|
4396
4433
|
"hls.js",
|
|
4397
4434
|
"mpegts.js/dist/mpegts.js"
|
|
4398
4435
|
]);
|
|
4436
|
+
if (options.qrcode) addViteOptimizeDepsInclude(bundlerOptions, app, ["qrcode"]);
|
|
4399
4437
|
},
|
|
4400
4438
|
extendsMarkdown: async (md, app) => {
|
|
4401
4439
|
linksPlugin(md);
|
package/lib/shared/index.d.ts
CHANGED
|
@@ -499,6 +499,12 @@ interface MarkdownPowerPluginOptions {
|
|
|
499
499
|
* @default false
|
|
500
500
|
*/
|
|
501
501
|
table?: boolean | TableContainerOptions;
|
|
502
|
+
/**
|
|
503
|
+
* 是否启用 二维码 嵌入语法
|
|
504
|
+
*
|
|
505
|
+
* @default false
|
|
506
|
+
*/
|
|
507
|
+
qrcode?: boolean;
|
|
502
508
|
/**
|
|
503
509
|
* 是否启用 自动填充 图片宽高属性
|
|
504
510
|
*
|
|
@@ -519,6 +525,89 @@ interface MarkdownPowerPluginOptions {
|
|
|
519
525
|
imageSize?: boolean | 'local' | 'all';
|
|
520
526
|
}
|
|
521
527
|
//#endregion
|
|
528
|
+
//#region src/shared/qrcode.d.ts
|
|
529
|
+
interface QRCodeMeta extends QRCodeProps {
|
|
530
|
+
/**
|
|
531
|
+
* mode: 'card' 的别名
|
|
532
|
+
*/
|
|
533
|
+
card?: boolean;
|
|
534
|
+
}
|
|
535
|
+
interface QRCodeProps {
|
|
536
|
+
/**
|
|
537
|
+
* 二维码标题
|
|
538
|
+
* 作为 HTML 标签的 `title` 属性、`alt` 属性
|
|
539
|
+
*/
|
|
540
|
+
title?: string;
|
|
541
|
+
/**
|
|
542
|
+
* 二维码内容
|
|
543
|
+
*/
|
|
544
|
+
text?: string;
|
|
545
|
+
/**
|
|
546
|
+
* 二维码宽度
|
|
547
|
+
*/
|
|
548
|
+
width?: number | string;
|
|
549
|
+
/**
|
|
550
|
+
* 显示模式
|
|
551
|
+
* - img: 以图片的形式显示二维码
|
|
552
|
+
* - card: 以卡片的形式显示,卡片以左右布局,左侧二维码,右侧 标题 + 内容
|
|
553
|
+
* @default 'img'
|
|
554
|
+
*/
|
|
555
|
+
mode?: 'img' | 'card';
|
|
556
|
+
/**
|
|
557
|
+
* 在 card 模式下是否翻转布局
|
|
558
|
+
*/
|
|
559
|
+
reverse?: boolean;
|
|
560
|
+
/**
|
|
561
|
+
* 二维码的对齐方式
|
|
562
|
+
* @default 'left'
|
|
563
|
+
*/
|
|
564
|
+
align?: 'left' | 'center' | 'right';
|
|
565
|
+
/**
|
|
566
|
+
* 是否渲染为 SVG 格式的二维码
|
|
567
|
+
* 默认输出为 PNG 格式的 dataURL
|
|
568
|
+
* @default false
|
|
569
|
+
*/
|
|
570
|
+
svg?: boolean;
|
|
571
|
+
/**
|
|
572
|
+
* 纠错等级。
|
|
573
|
+
* 可能的取值为低、中、四分位、高,分别对应 L、M、Q、H。
|
|
574
|
+
* @default 'M'
|
|
575
|
+
*/
|
|
576
|
+
level?: 'L' | 'M' | 'Q' | 'H' | 'l' | 'm' | 'q' | 'h';
|
|
577
|
+
/**
|
|
578
|
+
* 二维码版本。若未指定,将自动计算更合适的值。
|
|
579
|
+
* 取值范围 1-40
|
|
580
|
+
*/
|
|
581
|
+
version?: number;
|
|
582
|
+
/**
|
|
583
|
+
* 用于遮蔽符号的掩码模式。
|
|
584
|
+
* 可能的取值为0、1、2、3、4、5、6、7。
|
|
585
|
+
* 若未指定,系统将自动计算更合适的值。
|
|
586
|
+
*/
|
|
587
|
+
mask?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
588
|
+
/**
|
|
589
|
+
* 定义静区应有多宽。
|
|
590
|
+
* @default 4
|
|
591
|
+
*/
|
|
592
|
+
margin?: number;
|
|
593
|
+
/**
|
|
594
|
+
* 缩放因子。值为1表示每个模块(黑点)对应1像素。
|
|
595
|
+
*/
|
|
596
|
+
scale?: number;
|
|
597
|
+
/**
|
|
598
|
+
* 暗色模块的颜色。值必须为十六进制格式(RGBA)。
|
|
599
|
+
* 注意:暗色应始终比浅色模块的颜色更深。
|
|
600
|
+
* @default '#000000ff'
|
|
601
|
+
*/
|
|
602
|
+
light?: string;
|
|
603
|
+
/**
|
|
604
|
+
* 亮色模块的颜色。值必须为十六进制格式(RGBA)。
|
|
605
|
+
* 注意:亮色应始终比暗色模块的颜色更浅。
|
|
606
|
+
* @default '#ffffffff'
|
|
607
|
+
*/
|
|
608
|
+
dark?: string;
|
|
609
|
+
}
|
|
610
|
+
//#endregion
|
|
522
611
|
//#region src/shared/replit.d.ts
|
|
523
612
|
interface ReplitTokenMeta extends SizeOptions {
|
|
524
613
|
title?: string;
|
|
@@ -563,4 +652,4 @@ interface ArtPlayerTokenMeta extends SizeOptions {
|
|
|
563
652
|
type?: string;
|
|
564
653
|
}
|
|
565
654
|
//#endregion
|
|
566
|
-
export { AcFunTokenMeta, ArtPlayerTokenMeta, BilibiliTokenMeta, CanIUseMode, CanIUseOptions, CanIUseTokenMeta, CodeSandboxTokenMeta, CodeTabsOptions, CodepenTokenMeta, DemoContainerRender, DemoFile, DemoMeta, FileTreeIconMode, FileTreeOptions, FontAwesomeAssetBuiltIn, FontAwesomePrefix, FontAwesomeProvider, IconAssetLink, IconFontProvider, IconOptions, IconProviderBase, IconifyPrefix, IconifyProvider, JSFiddleTokenMeta, LiteralUnion, MarkdownDemoEnv, MarkdownPowerPluginOptions, NpmToOptions, NpmToPackageManager, PDFEmbedType, PDFOptions, PDFTokenMeta, PlotOptions, ReplEditorData, ReplOptions, ReplitTokenMeta, SizeOptions, ThemeOptions, VideoOptions, YoutubeTokenMeta };
|
|
655
|
+
export { AcFunTokenMeta, ArtPlayerTokenMeta, BilibiliTokenMeta, CanIUseMode, CanIUseOptions, CanIUseTokenMeta, CodeSandboxTokenMeta, CodeTabsOptions, CodepenTokenMeta, DemoContainerRender, DemoFile, DemoMeta, FileTreeIconMode, FileTreeOptions, FontAwesomeAssetBuiltIn, FontAwesomePrefix, FontAwesomeProvider, IconAssetLink, IconFontProvider, IconOptions, IconProviderBase, IconifyPrefix, IconifyProvider, JSFiddleTokenMeta, LiteralUnion, MarkdownDemoEnv, MarkdownPowerPluginOptions, NpmToOptions, NpmToPackageManager, PDFEmbedType, PDFOptions, PDFTokenMeta, PlotOptions, QRCodeMeta, QRCodeProps, ReplEditorData, ReplOptions, ReplitTokenMeta, SizeOptions, ThemeOptions, VideoOptions, YoutubeTokenMeta };
|
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.182",
|
|
5
5
|
"description": "The Plugin for VuePress 2 - markdown power",
|
|
6
6
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"artplayer": "^5.3.0",
|
|
35
35
|
"dashjs": "^5.1.0",
|
|
36
|
-
"esbuild": "^0.27.
|
|
36
|
+
"esbuild": "^0.27.1",
|
|
37
37
|
"hls.js": "^1.6.15",
|
|
38
38
|
"less": "^4.4.2",
|
|
39
39
|
"markdown-it": "^14.1.0",
|
|
40
40
|
"mpegts.js": "^1.7.3",
|
|
41
41
|
"pyodide": "^0.29.0",
|
|
42
|
-
"sass": "^1.
|
|
43
|
-
"sass-embedded": "^1.
|
|
42
|
+
"sass": "^1.96.0",
|
|
43
|
+
"sass-embedded": "^1.96.0",
|
|
44
44
|
"stylus": "^0.64.0",
|
|
45
45
|
"vuepress": "2.0.0-rc.26"
|
|
46
46
|
},
|
|
@@ -77,12 +77,12 @@
|
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@mdit/plugin-attrs": "^0.
|
|
80
|
+
"@mdit/plugin-attrs": "^0.24.1",
|
|
81
81
|
"@mdit/plugin-footnote": "^0.22.3",
|
|
82
82
|
"@mdit/plugin-mark": "^0.22.1",
|
|
83
|
-
"@mdit/plugin-sub": "^0.
|
|
84
|
-
"@mdit/plugin-sup": "^0.
|
|
85
|
-
"@mdit/plugin-tab": "^0.
|
|
83
|
+
"@mdit/plugin-sub": "^0.23.0",
|
|
84
|
+
"@mdit/plugin-sup": "^0.23.0",
|
|
85
|
+
"@mdit/plugin-tab": "^0.23.0",
|
|
86
86
|
"@mdit/plugin-tasklist": "^0.22.2",
|
|
87
87
|
"@pengzhanbo/utils": "^2.1.2",
|
|
88
88
|
"@vuepress/helper": "2.0.0-rc.120",
|
|
@@ -91,9 +91,11 @@
|
|
|
91
91
|
"image-size": "^2.0.2",
|
|
92
92
|
"local-pkg": "^1.1.2",
|
|
93
93
|
"lru-cache": "^11.2.4",
|
|
94
|
+
"markdown-it-cjk-friendly": "^1.3.2",
|
|
94
95
|
"markdown-it-container": "^4.0.0",
|
|
95
96
|
"nanoid": "^5.1.6",
|
|
96
|
-
"
|
|
97
|
+
"qrcode": "^1.5.4",
|
|
98
|
+
"shiki": "^3.20.0",
|
|
97
99
|
"tm-grammars": "^1.26.0",
|
|
98
100
|
"tm-themes": "^1.10.13",
|
|
99
101
|
"vue": "^3.5.25"
|