vuepress-plugin-md-power 1.0.0-rc.170 → 1.0.0-rc.172
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,108 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted } from "vue";
|
|
2
|
+
import { onContentUpdated, useRouter } from "vuepress/client";
|
|
3
|
+
|
|
4
|
+
//#region src/client/composables/mark.ts
|
|
5
|
+
const MARK_MODE_ATTR = "data-mark-mode";
|
|
6
|
+
const MARK_MODE_LAZY = "lazy";
|
|
7
|
+
const MARK_VISIBLE_CLASS = "vp-mark-visible";
|
|
8
|
+
const MARK_BOUND_ATTR = "data-vp-mark-bound";
|
|
9
|
+
const MARK_SELECTOR = ".vp-doc mark";
|
|
10
|
+
const DOC_SELECTOR = ".vp-doc";
|
|
11
|
+
const BOUND_SELECTOR = `${MARK_SELECTOR}[${MARK_BOUND_ATTR}="1"]`;
|
|
12
|
+
function setupMarkHighlight(mode) {
|
|
13
|
+
if (typeof window === "undefined" || __VUEPRESS_SSR__) return;
|
|
14
|
+
const root = document.documentElement;
|
|
15
|
+
if (mode !== MARK_MODE_LAZY) {
|
|
16
|
+
root.removeAttribute(MARK_MODE_ATTR);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
root.setAttribute(MARK_MODE_ATTR, MARK_MODE_LAZY);
|
|
20
|
+
let intersectionObserver = null;
|
|
21
|
+
let mutationObserver = null;
|
|
22
|
+
let rafId = null;
|
|
23
|
+
let removeAfterEach = null;
|
|
24
|
+
const ensureObserver = () => {
|
|
25
|
+
if (!intersectionObserver) intersectionObserver = new IntersectionObserver((entries, obs) => {
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (!entry.isIntersecting && entry.intersectionRatio <= 0) continue;
|
|
28
|
+
const target = entry.target;
|
|
29
|
+
target.classList.add(MARK_VISIBLE_CLASS);
|
|
30
|
+
target.removeAttribute(MARK_BOUND_ATTR);
|
|
31
|
+
obs.unobserve(target);
|
|
32
|
+
}
|
|
33
|
+
}, {
|
|
34
|
+
threshold: [
|
|
35
|
+
0,
|
|
36
|
+
.1,
|
|
37
|
+
.25,
|
|
38
|
+
.5
|
|
39
|
+
],
|
|
40
|
+
rootMargin: "8% 0px -8% 0px"
|
|
41
|
+
});
|
|
42
|
+
return intersectionObserver;
|
|
43
|
+
};
|
|
44
|
+
const bindMarks = () => {
|
|
45
|
+
const marks = Array.from(document.querySelectorAll(MARK_SELECTOR)).filter((mark) => mark instanceof HTMLElement && !mark.classList.contains(MARK_VISIBLE_CLASS) && mark.getAttribute(MARK_BOUND_ATTR) !== "1");
|
|
46
|
+
if (marks.length === 0) return;
|
|
47
|
+
const observer = ensureObserver();
|
|
48
|
+
for (const mark of marks) {
|
|
49
|
+
mark.setAttribute(MARK_BOUND_ATTR, "1");
|
|
50
|
+
observer.observe(mark);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const scheduleBind = () => {
|
|
54
|
+
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
55
|
+
rafId = requestAnimationFrame(() => {
|
|
56
|
+
rafId = null;
|
|
57
|
+
bindMarks();
|
|
58
|
+
});
|
|
59
|
+
};
|
|
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
|
+
const resetObserver = () => {
|
|
73
|
+
document.querySelectorAll(BOUND_SELECTOR).forEach((mark) => {
|
|
74
|
+
if (!mark.classList.contains(MARK_VISIBLE_CLASS)) mark.removeAttribute(MARK_BOUND_ATTR);
|
|
75
|
+
});
|
|
76
|
+
if (intersectionObserver) {
|
|
77
|
+
intersectionObserver.disconnect();
|
|
78
|
+
intersectionObserver = null;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const router = useRouter();
|
|
82
|
+
onMounted(() => {
|
|
83
|
+
observeDocMutations();
|
|
84
|
+
scheduleBind();
|
|
85
|
+
});
|
|
86
|
+
onContentUpdated(() => {
|
|
87
|
+
resetObserver();
|
|
88
|
+
observeDocMutations();
|
|
89
|
+
scheduleBind();
|
|
90
|
+
});
|
|
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
|
+
}
|
|
106
|
+
|
|
107
|
+
//#endregion
|
|
108
|
+
export { setupMarkHighlight };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ensureEndingSlash, isLinkHttp } from "vuepress/shared";
|
|
2
|
-
import { isMobile, isSafari, isiPad } from "@vuepress/helper/client";
|
|
3
2
|
import { withBase } from "vuepress/client";
|
|
3
|
+
import { isMobile, isSafari, isiPad } from "@vuepress/helper/client";
|
|
4
4
|
import { pluginOptions } from "../options.js";
|
|
5
5
|
|
|
6
6
|
//#region src/client/composables/pdf.ts
|
package/lib/node/index.d.ts
CHANGED
|
@@ -210,6 +210,9 @@ interface CodeTreeOptions {
|
|
|
210
210
|
height?: string | number;
|
|
211
211
|
}
|
|
212
212
|
//#endregion
|
|
213
|
+
//#region src/shared/mark.d.ts
|
|
214
|
+
type MarkOptions = 'lazy' | 'eager';
|
|
215
|
+
//#endregion
|
|
213
216
|
//#region src/shared/repl.d.ts
|
|
214
217
|
type ThemeOptions = BuiltinTheme | {
|
|
215
218
|
light: BuiltinTheme;
|
|
@@ -279,6 +282,11 @@ interface MarkdownPowerPluginOptions {
|
|
|
279
282
|
* @default false
|
|
280
283
|
*/
|
|
281
284
|
abbr?: boolean;
|
|
285
|
+
/**
|
|
286
|
+
* 马克笔动画模式
|
|
287
|
+
* @default 'eager'
|
|
288
|
+
*/
|
|
289
|
+
mark?: MarkOptions;
|
|
282
290
|
/**
|
|
283
291
|
* 配置代码块分组
|
|
284
292
|
*/
|
package/lib/node/index.js
CHANGED
|
@@ -6,9 +6,8 @@ import http from "node:https";
|
|
|
6
6
|
import { URL, URLSearchParams } from "node:url";
|
|
7
7
|
import { camelCase, isBoolean, isEmptyObject, isNull, isNumber, isPlainObject as isPlainObject$1, isString, isUndefined, kebabCase, notNullish, omit, toArray, uniqueBy, withTimeout } from "@pengzhanbo/utils";
|
|
8
8
|
import imageSize from "image-size";
|
|
9
|
-
import { colors, fs, getDirname, logger, ora, path } from "vuepress/utils";
|
|
9
|
+
import { colors, fs, getDirname, logger, ora, path, tinyglobby } from "vuepress/utils";
|
|
10
10
|
import path$1 from "node:path";
|
|
11
|
-
import { globSync } from "tinyglobby";
|
|
12
11
|
import { isLinkHttp as isLinkHttp$1, isLinkWithProtocol, removeEndingSlash, removeLeadingSlash } from "vuepress/shared";
|
|
13
12
|
import fs$1, { promises } from "node:fs";
|
|
14
13
|
import process from "node:process";
|
|
@@ -1474,7 +1473,7 @@ function codeTreePlugin(md, app, options = {}) {
|
|
|
1474
1473
|
content: ({ dir, icon,...props }, _, env) => {
|
|
1475
1474
|
const codeTreeFiles = env.codeTreeFiles ??= [];
|
|
1476
1475
|
const root = findFile(app, env, dir);
|
|
1477
|
-
const files = globSync("**/*", {
|
|
1476
|
+
const files = tinyglobby.globSync("**/*", {
|
|
1478
1477
|
cwd: root,
|
|
1479
1478
|
onlyFiles: true,
|
|
1480
1479
|
dot: true,
|
|
@@ -3961,7 +3960,7 @@ const iconPlugin = (md, options = {}) => {
|
|
|
3961
3960
|
//#endregion
|
|
3962
3961
|
//#region src/node/icon/prepareIcon.ts
|
|
3963
3962
|
function getFontAwesomeCDNLink(type) {
|
|
3964
|
-
return `https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free
|
|
3963
|
+
return `https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/js/${type}.min.js`;
|
|
3965
3964
|
}
|
|
3966
3965
|
function prepareIcon(imports, options = {}) {
|
|
3967
3966
|
const setupContent = [];
|
|
@@ -3974,7 +3973,7 @@ function prepareIcon(imports, options = {}) {
|
|
|
3974
3973
|
"fontawesome"
|
|
3975
3974
|
].map(getFontAwesomeCDNLink).map((asset$1) => normalizeAsset(asset$1, "fontawesome"));
|
|
3976
3975
|
if (asset === "fontawesome-with-brands") return normalizeAsset(getFontAwesomeCDNLink("brands"), "fontawesome");
|
|
3977
|
-
return
|
|
3976
|
+
return normalizeAsset(asset, "fontawesome");
|
|
3978
3977
|
}).flat().filter(notNullish));
|
|
3979
3978
|
let hasStyle = false;
|
|
3980
3979
|
let hasScript = false;
|
|
@@ -4331,6 +4330,13 @@ async function prepareConfigFile(app, options) {
|
|
|
4331
4330
|
enhances.add(`app.component('VPTable', VPTable)`);
|
|
4332
4331
|
}
|
|
4333
4332
|
const setupIcon = prepareIcon(imports, options.icon);
|
|
4333
|
+
const setupStmts = [];
|
|
4334
|
+
const iconSetup = setupIcon.trim();
|
|
4335
|
+
if (iconSetup) setupStmts.push(iconSetup);
|
|
4336
|
+
const markMode = options.mark === "lazy" ? "lazy" : "eager";
|
|
4337
|
+
imports.add(`import { setupMarkHighlight } from '${CLIENT_FOLDER}composables/mark.js'`);
|
|
4338
|
+
setupStmts.push(`setupMarkHighlight(${JSON.stringify(markMode)})`);
|
|
4339
|
+
const setupContent = setupStmts.length ? ` ${setupStmts.join("\n ")}\n` : "";
|
|
4334
4340
|
return app.writeTemp("md-power/config.js", `\
|
|
4335
4341
|
import { defineClientConfig } from 'vuepress/client'
|
|
4336
4342
|
${Array.from(imports.values()).join("\n")}
|
|
@@ -4342,7 +4348,7 @@ export default defineClientConfig({
|
|
|
4342
4348
|
${Array.from(enhances.values()).map((item) => ` ${item}`).join("\n")}
|
|
4343
4349
|
},
|
|
4344
4350
|
setup() {
|
|
4345
|
-
${
|
|
4351
|
+
${setupContent}
|
|
4346
4352
|
}
|
|
4347
4353
|
})
|
|
4348
4354
|
`);
|
package/lib/shared/index.d.ts
CHANGED
|
@@ -208,6 +208,9 @@ interface CodeTreeOptions {
|
|
|
208
208
|
height?: string | number;
|
|
209
209
|
}
|
|
210
210
|
//#endregion
|
|
211
|
+
//#region src/shared/mark.d.ts
|
|
212
|
+
type MarkOptions = 'lazy' | 'eager';
|
|
213
|
+
//#endregion
|
|
211
214
|
//#region src/shared/repl.d.ts
|
|
212
215
|
type ThemeOptions = BuiltinTheme | {
|
|
213
216
|
light: BuiltinTheme;
|
|
@@ -277,6 +280,11 @@ interface MarkdownPowerPluginOptions {
|
|
|
277
280
|
* @default false
|
|
278
281
|
*/
|
|
279
282
|
abbr?: boolean;
|
|
283
|
+
/**
|
|
284
|
+
* 马克笔动画模式
|
|
285
|
+
* @default 'eager'
|
|
286
|
+
*/
|
|
287
|
+
mark?: MarkOptions;
|
|
280
288
|
/**
|
|
281
289
|
* 配置代码块分组
|
|
282
290
|
*/
|
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.172",
|
|
5
5
|
"description": "The Plugin for VuePress 2 - markdown power",
|
|
6
6
|
"author": "pengzhanbo <volodymyr@foxmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"artplayer": "^5.3.0",
|
|
35
35
|
"dashjs": "^5.0.3",
|
|
36
|
-
"esbuild": "^0.25.
|
|
36
|
+
"esbuild": "^0.25.11",
|
|
37
37
|
"hls.js": "^1.6.13",
|
|
38
38
|
"less": "^4.4.2",
|
|
39
39
|
"markdown-it": "^14.1.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"sass": "^1.93.2",
|
|
43
43
|
"sass-embedded": "^1.93.2",
|
|
44
44
|
"stylus": "^0.64.0",
|
|
45
|
-
"vuepress": "2.0.0-rc.
|
|
45
|
+
"vuepress": "2.0.0-rc.26"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
48
48
|
"artplayer": {
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"@mdit/plugin-tab": "^0.22.2",
|
|
86
86
|
"@mdit/plugin-tasklist": "^0.22.1",
|
|
87
87
|
"@pengzhanbo/utils": "^2.1.0",
|
|
88
|
-
"@vuepress/helper": "2.0.0-rc.
|
|
88
|
+
"@vuepress/helper": "2.0.0-rc.114",
|
|
89
89
|
"@vueuse/core": "^13.9.0",
|
|
90
90
|
"chokidar": "4.0.3",
|
|
91
91
|
"image-size": "^2.0.2",
|
|
@@ -94,9 +94,8 @@
|
|
|
94
94
|
"markdown-it-container": "^4.0.0",
|
|
95
95
|
"nanoid": "^5.1.6",
|
|
96
96
|
"shiki": "^3.13.0",
|
|
97
|
-
"
|
|
98
|
-
"tm-
|
|
99
|
-
"tm-themes": "^1.10.9",
|
|
97
|
+
"tm-grammars": "^1.24.22",
|
|
98
|
+
"tm-themes": "^1.10.11",
|
|
100
99
|
"vue": "^3.5.22"
|
|
101
100
|
},
|
|
102
101
|
"devDependencies": {
|