vuepress-plugin-md-power 1.0.0-rc.96 → 1.0.0-rc.98
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/CanIUse.vue +1 -1
- package/lib/client/components/CodeEditor.vue +2 -2
- package/lib/client/components/CodeRepl.vue +2 -2
- package/lib/client/components/FileTreeItem.vue +22 -4
- package/lib/client/components/PDFViewer.vue +2 -2
- package/lib/client/components/Plot.vue +2 -2
- package/lib/client/components/Replit.vue +1 -1
- package/lib/client/composables/pdf.js +2 -2
- package/lib/client/utils/link.js +1 -1
- package/lib/node/index.d.ts +181 -83
- package/lib/node/index.js +865 -685
- package/lib/shared/index.d.ts +172 -81
- package/package.json +5 -4
package/lib/node/index.js
CHANGED
|
@@ -1,3 +1,211 @@
|
|
|
1
|
+
// src/node/features/imageSize.ts
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
3
|
+
import http from "node:https";
|
|
4
|
+
import { URL } from "node:url";
|
|
5
|
+
import { isLinkExternal, isLinkHttp } from "@vuepress/helper";
|
|
6
|
+
import imageSize from "image-size";
|
|
7
|
+
import { fs, path } from "vuepress/utils";
|
|
8
|
+
|
|
9
|
+
// src/node/utils/resolveAttrs.ts
|
|
10
|
+
var RE_ATTR_VALUE = /(?:^|\s+)(?<attr>[\w-]+)(?:=\s*(?<quote>['"])(?<value>.+?)\k<quote>)?(?:\s+|$)/;
|
|
11
|
+
function resolveAttrs(info) {
|
|
12
|
+
info = info.trim();
|
|
13
|
+
if (!info)
|
|
14
|
+
return { rawAttrs: "", attrs: {} };
|
|
15
|
+
const attrs = {};
|
|
16
|
+
const rawAttrs = info;
|
|
17
|
+
let matched;
|
|
18
|
+
while (matched = info.match(RE_ATTR_VALUE)) {
|
|
19
|
+
const { attr, value } = matched.groups || {};
|
|
20
|
+
attrs[attr] = value ?? true;
|
|
21
|
+
info = info.slice(matched[0].length);
|
|
22
|
+
}
|
|
23
|
+
Object.keys(attrs).forEach((key) => {
|
|
24
|
+
let value = attrs[key];
|
|
25
|
+
value = typeof value === "string" ? value.trim() : value;
|
|
26
|
+
if (value === "true")
|
|
27
|
+
value = true;
|
|
28
|
+
else if (value === "false")
|
|
29
|
+
value = false;
|
|
30
|
+
attrs[key] = value;
|
|
31
|
+
if (key.includes("-")) {
|
|
32
|
+
const _key = key.replace(/-(\w)/g, (_, c) => c.toUpperCase());
|
|
33
|
+
attrs[_key] = value;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return { attrs, rawAttrs };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/node/features/imageSize.ts
|
|
40
|
+
var REG_IMG = /!\[.*?\]\(.*?\)/g;
|
|
41
|
+
var REG_IMG_TAG = /<img(.*?)>/g;
|
|
42
|
+
var REG_IMG_TAG_SRC = /src(?:set)?=(['"])(.+?)\1/g;
|
|
43
|
+
var BADGE_LIST = [
|
|
44
|
+
"https://img.shields.io",
|
|
45
|
+
"https://badge.fury.io",
|
|
46
|
+
"https://badgen.net",
|
|
47
|
+
"https://forthebadge.com",
|
|
48
|
+
"https://vercel.com/button"
|
|
49
|
+
];
|
|
50
|
+
var cache = /* @__PURE__ */ new Map();
|
|
51
|
+
async function imageSizePlugin(app, md, type2 = false) {
|
|
52
|
+
if (!app.env.isBuild || !type2)
|
|
53
|
+
return;
|
|
54
|
+
if (type2 === "all") {
|
|
55
|
+
try {
|
|
56
|
+
await scanRemoteImageSize(app);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const imageRule = md.renderer.rules.image;
|
|
61
|
+
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
|
62
|
+
if (!env.filePathRelative || !env.filePath)
|
|
63
|
+
return imageRule(tokens, idx, options, env, self);
|
|
64
|
+
const token = tokens[idx];
|
|
65
|
+
const src = token.attrGet("src");
|
|
66
|
+
const width = token.attrGet("width");
|
|
67
|
+
const height = token.attrGet("height");
|
|
68
|
+
const size = resolveSize(src, width, height, env);
|
|
69
|
+
if (size) {
|
|
70
|
+
token.attrSet("width", `${size.width}`);
|
|
71
|
+
token.attrSet("height", `${size.height}`);
|
|
72
|
+
}
|
|
73
|
+
return imageRule(tokens, idx, options, env, self);
|
|
74
|
+
};
|
|
75
|
+
const rawHtmlBlockRule = md.renderer.rules.html_block;
|
|
76
|
+
const rawHtmlInlineRule = md.renderer.rules.html_inline;
|
|
77
|
+
md.renderer.rules.html_block = createHtmlRule(rawHtmlBlockRule);
|
|
78
|
+
md.renderer.rules.html_inline = createHtmlRule(rawHtmlInlineRule);
|
|
79
|
+
function createHtmlRule(rawHtmlRule) {
|
|
80
|
+
return (tokens, idx, options, env, self) => {
|
|
81
|
+
const token = tokens[idx];
|
|
82
|
+
token.content = token.content.replace(REG_IMG_TAG, (raw, info) => {
|
|
83
|
+
const { attrs } = resolveAttrs(info);
|
|
84
|
+
const src = attrs.src || attrs.srcset;
|
|
85
|
+
const size = resolveSize(src, attrs.width, attrs.height, env);
|
|
86
|
+
if (!size)
|
|
87
|
+
return raw;
|
|
88
|
+
attrs.width = size.width;
|
|
89
|
+
attrs.height = size.height;
|
|
90
|
+
const imgAttrs = Object.entries(attrs).map(([key, value]) => typeof value === "boolean" ? key : `${key}="${value}"`).join(" ");
|
|
91
|
+
return `<img ${imgAttrs}>`;
|
|
92
|
+
});
|
|
93
|
+
return rawHtmlRule(tokens, idx, options, env, self);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function resolveSize(src, width, height, env) {
|
|
97
|
+
if (!src || src.startsWith("data:"))
|
|
98
|
+
return false;
|
|
99
|
+
if (width && height)
|
|
100
|
+
return false;
|
|
101
|
+
const isExternal = isLinkExternal(src, env.base);
|
|
102
|
+
const filepath2 = isExternal ? src : resolveImageUrl(src, env, app);
|
|
103
|
+
if (isExternal) {
|
|
104
|
+
if (!cache.has(filepath2))
|
|
105
|
+
return false;
|
|
106
|
+
} else {
|
|
107
|
+
if (!cache.has(filepath2)) {
|
|
108
|
+
if (!fs.existsSync(filepath2))
|
|
109
|
+
return false;
|
|
110
|
+
const { width: w, height: h } = imageSize(filepath2);
|
|
111
|
+
if (!w || !h)
|
|
112
|
+
return false;
|
|
113
|
+
cache.set(filepath2, { width: w, height: h });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const { width: originalWidth, height: originalHeight } = cache.get(filepath2);
|
|
117
|
+
const ratio = originalWidth / originalHeight;
|
|
118
|
+
if (width && !height) {
|
|
119
|
+
const w = Number.parseInt(width, 10);
|
|
120
|
+
return { width: w, height: Math.round(w / ratio) };
|
|
121
|
+
} else if (height && !width) {
|
|
122
|
+
const h = Number.parseInt(height, 10);
|
|
123
|
+
return { width: Math.round(h * ratio), height: h };
|
|
124
|
+
} else {
|
|
125
|
+
return { width: originalWidth, height: originalHeight };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function resolveImageUrl(src, env, app) {
|
|
130
|
+
if (src[0] === "/")
|
|
131
|
+
return app.dir.public(src.slice(1));
|
|
132
|
+
if (env.filePathRelative && src[0] === ".")
|
|
133
|
+
return app.dir.source(path.join(path.dirname(env.filePathRelative), src));
|
|
134
|
+
if (env.filePath && (src[0] === "." || src[0] === "/"))
|
|
135
|
+
return path.resolve(env.filePath, src);
|
|
136
|
+
return path.resolve(src);
|
|
137
|
+
}
|
|
138
|
+
async function scanRemoteImageSize(app) {
|
|
139
|
+
if (!app.env.isBuild)
|
|
140
|
+
return;
|
|
141
|
+
const cwd = app.dir.source();
|
|
142
|
+
const files = await fs.readdir(cwd, { recursive: true });
|
|
143
|
+
const imgList = [];
|
|
144
|
+
for (const file of files) {
|
|
145
|
+
const filepath2 = path.join(cwd, file);
|
|
146
|
+
if ((await fs.stat(filepath2)).isFile() && !filepath2.includes(".vuepress") && !filepath2.includes("node_modules") && filepath2.endsWith(".md")) {
|
|
147
|
+
const content = await fs.readFile(filepath2, "utf-8");
|
|
148
|
+
const syntaxMatched = content.match(REG_IMG) ?? [];
|
|
149
|
+
for (const img of syntaxMatched) {
|
|
150
|
+
const src = img.slice(img.indexOf("](") + 2, -1);
|
|
151
|
+
addList(src.split(/\s+/)[0]);
|
|
152
|
+
}
|
|
153
|
+
const tagMatched = content.match(REG_IMG_TAG) ?? [];
|
|
154
|
+
for (const img of tagMatched) {
|
|
155
|
+
const src = img.match(REG_IMG_TAG_SRC)?.[2] ?? "";
|
|
156
|
+
addList(src);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function addList(src) {
|
|
161
|
+
if (src && isLinkHttp(src) && !imgList.includes(src) && !BADGE_LIST.some((badge) => src.startsWith(badge))) {
|
|
162
|
+
imgList.push(src);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
await Promise.all(imgList.map(async (src) => {
|
|
166
|
+
if (!cache.has(src)) {
|
|
167
|
+
const { width, height } = await fetchImageSize(src);
|
|
168
|
+
if (width && height)
|
|
169
|
+
cache.set(src, { width, height });
|
|
170
|
+
}
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
function fetchImageSize(src) {
|
|
174
|
+
const link = new URL(src);
|
|
175
|
+
return new Promise((resolve) => {
|
|
176
|
+
http.get(link, async (stream) => {
|
|
177
|
+
const chunks = [];
|
|
178
|
+
for await (const chunk of stream) {
|
|
179
|
+
chunks.push(chunk);
|
|
180
|
+
try {
|
|
181
|
+
const { width: width2, height: height2 } = imageSize(Buffer.concat(chunks));
|
|
182
|
+
if (width2 && height2) {
|
|
183
|
+
return resolve({ width: width2, height: height2 });
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const { width, height } = imageSize(Buffer.concat(chunks));
|
|
189
|
+
resolve({ width, height });
|
|
190
|
+
}).on("error", () => resolve({ width: 0, height: 0 }));
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function resolveImageSize(app, url, remote = false) {
|
|
194
|
+
if (cache.has(url))
|
|
195
|
+
return cache.get(url);
|
|
196
|
+
if (isLinkHttp(url) && remote) {
|
|
197
|
+
return await fetchImageSize(url);
|
|
198
|
+
}
|
|
199
|
+
if (url[0] === "/") {
|
|
200
|
+
const filepath2 = app.dir.public(url.slice(1));
|
|
201
|
+
if (fs.existsSync(filepath2)) {
|
|
202
|
+
const { width, height } = imageSize(filepath2);
|
|
203
|
+
return { width, height };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return { width: 0, height: 0 };
|
|
207
|
+
}
|
|
208
|
+
|
|
1
209
|
// src/node/plugin.ts
|
|
2
210
|
import { addViteOptimizeDepsInclude } from "@vuepress/helper";
|
|
3
211
|
|
|
@@ -115,39 +323,6 @@ function resolveVersions(versions) {
|
|
|
115
323
|
};
|
|
116
324
|
}
|
|
117
325
|
|
|
118
|
-
// src/node/features/pdf.ts
|
|
119
|
-
import { path } from "vuepress/utils";
|
|
120
|
-
|
|
121
|
-
// src/node/utils/resolveAttrs.ts
|
|
122
|
-
var RE_ATTR_VALUE = /(?:^|\s+)(?<attr>[\w-]+)(?:=\s*(?<quote>['"])(?<value>.+?)\k<quote>)?(?:\s+|$)/;
|
|
123
|
-
function resolveAttrs(info) {
|
|
124
|
-
info = info.trim();
|
|
125
|
-
if (!info)
|
|
126
|
-
return { rawAttrs: "", attrs: {} };
|
|
127
|
-
const attrs = {};
|
|
128
|
-
const rawAttrs = info;
|
|
129
|
-
let matched;
|
|
130
|
-
while (matched = info.match(RE_ATTR_VALUE)) {
|
|
131
|
-
const { attr, value } = matched.groups || {};
|
|
132
|
-
attrs[attr] = value ?? true;
|
|
133
|
-
info = info.slice(matched[0].length);
|
|
134
|
-
}
|
|
135
|
-
Object.keys(attrs).forEach((key) => {
|
|
136
|
-
let value = attrs[key];
|
|
137
|
-
value = typeof value === "string" ? value.trim() : value;
|
|
138
|
-
if (value === "true")
|
|
139
|
-
value = true;
|
|
140
|
-
else if (value === "false")
|
|
141
|
-
value = false;
|
|
142
|
-
attrs[key] = value;
|
|
143
|
-
if (key.includes("-")) {
|
|
144
|
-
const _key = key.replace(/-(\w)/g, (_, c) => c.toUpperCase());
|
|
145
|
-
attrs[_key] = value;
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
return { attrs, rawAttrs };
|
|
149
|
-
}
|
|
150
|
-
|
|
151
326
|
// src/node/utils/parseRect.ts
|
|
152
327
|
function parseRect(str, unit = "px") {
|
|
153
328
|
if (Number.parseFloat(str) === Number(str))
|
|
@@ -155,271 +330,67 @@ function parseRect(str, unit = "px") {
|
|
|
155
330
|
return str;
|
|
156
331
|
}
|
|
157
332
|
|
|
158
|
-
// src/node/features/
|
|
159
|
-
var
|
|
333
|
+
// src/node/features/codepen.ts
|
|
334
|
+
var CODEPEN_LINK = "https://codepen.io/";
|
|
335
|
+
var codepenPlugin = (md) => {
|
|
160
336
|
createRuleBlock(md, {
|
|
161
|
-
type: "
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
meta([, page, info = "", src = ""]) {
|
|
337
|
+
type: "codepen",
|
|
338
|
+
syntaxPattern: /^@\[codepen([^\]]*)\]\(([^)]*)\)/,
|
|
339
|
+
meta: ([, info = "", source = ""]) => {
|
|
165
340
|
const { attrs } = resolveAttrs(info);
|
|
341
|
+
const [user, slash] = source.split("/");
|
|
166
342
|
return {
|
|
167
|
-
src,
|
|
168
|
-
page: +page || 1,
|
|
169
|
-
noToolbar: Boolean(attrs.noToolbar ?? false),
|
|
170
|
-
zoom: +attrs.zoom || 50,
|
|
171
343
|
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
172
|
-
height: attrs.height ? parseRect(attrs.height) : "",
|
|
173
|
-
|
|
174
|
-
|
|
344
|
+
height: attrs.height ? parseRect(attrs.height) : "400px",
|
|
345
|
+
user,
|
|
346
|
+
slash,
|
|
347
|
+
title: attrs.title,
|
|
348
|
+
preview: attrs.preview,
|
|
349
|
+
editable: attrs.editable,
|
|
350
|
+
tab: attrs.tab ?? "result",
|
|
351
|
+
theme: attrs.theme
|
|
175
352
|
};
|
|
176
353
|
},
|
|
177
|
-
content(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// src/node/features/icons.ts
|
|
184
|
-
var [openTag, endTag] = [":[", "]:"];
|
|
185
|
-
function createTokenizer() {
|
|
186
|
-
return (state, silent) => {
|
|
187
|
-
let found = false;
|
|
188
|
-
const max = state.posMax;
|
|
189
|
-
const start = state.pos;
|
|
190
|
-
if (state.src.slice(start, start + 2) !== openTag)
|
|
191
|
-
return false;
|
|
192
|
-
if (silent)
|
|
193
|
-
return false;
|
|
194
|
-
if (max - start < 5)
|
|
195
|
-
return false;
|
|
196
|
-
state.pos = start + 2;
|
|
197
|
-
while (state.pos < max) {
|
|
198
|
-
if (state.src.slice(state.pos, state.pos + 2) === endTag) {
|
|
199
|
-
found = true;
|
|
200
|
-
break;
|
|
354
|
+
content: (meta) => {
|
|
355
|
+
const { title = "Codepen", height, width } = meta;
|
|
356
|
+
const params = new URLSearchParams();
|
|
357
|
+
if (meta.editable) {
|
|
358
|
+
params.set("editable", "true");
|
|
201
359
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return
|
|
360
|
+
if (meta.tab) {
|
|
361
|
+
params.set("default-tab", meta.tab);
|
|
362
|
+
}
|
|
363
|
+
if (meta.theme) {
|
|
364
|
+
params.set("theme-id", meta.theme);
|
|
365
|
+
}
|
|
366
|
+
const middle = meta.preview ? "/embed/preview/" : "/embed/";
|
|
367
|
+
const link = `${CODEPEN_LINK}${meta.user}${middle}${meta.slash}?${params.toString()}`;
|
|
368
|
+
const style = `width:${width};height:${height};margin:16px auto;border-radius:5px;`;
|
|
369
|
+
return `<iframe class="code-pen-iframe-wrapper" src="${link}" title="${title}" style="${style}" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="${CODEPEN_LINK}${meta.user}/pen/${meta.slash}">${title}</a> by ${meta.user} (<a href="${CODEPEN_LINK}${meta.user}">@${meta.user}</a>) on <a href="${CODEPEN_LINK}">CodePen</a>.</iframe>`;
|
|
212
370
|
}
|
|
213
|
-
|
|
214
|
-
state.pos = start + 2;
|
|
215
|
-
const [name, options = ""] = content.split(/\s+/);
|
|
216
|
-
const [size, color] = options.split("/");
|
|
217
|
-
const icon = state.push("vp_iconify_open", "VPIcon", 1);
|
|
218
|
-
icon.markup = openTag;
|
|
219
|
-
if (name)
|
|
220
|
-
icon.attrSet("name", name);
|
|
221
|
-
if (size)
|
|
222
|
-
icon.attrSet("size", size);
|
|
223
|
-
if (color)
|
|
224
|
-
icon.attrSet("color", color);
|
|
225
|
-
const close = state.push("vp_iconify_close", "VPIcon", -1);
|
|
226
|
-
close.markup = endTag;
|
|
227
|
-
state.pos = state.posMax + 2;
|
|
228
|
-
state.posMax = max;
|
|
229
|
-
return true;
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
var iconsPlugin = (md) => {
|
|
233
|
-
md.inline.ruler.before("emphasis", "iconify", createTokenizer());
|
|
371
|
+
});
|
|
234
372
|
};
|
|
235
373
|
|
|
236
|
-
// src/node/features/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// src/node/utils/timeToSeconds.ts
|
|
240
|
-
function timeToSeconds(time) {
|
|
241
|
-
if (!time)
|
|
242
|
-
return 0;
|
|
243
|
-
if (Number.parseFloat(time) === Number(time))
|
|
244
|
-
return Number(time);
|
|
245
|
-
const [s, m, h] = time.split(":").reverse().map((n) => Number(n) || 0);
|
|
246
|
-
return s + m * 60 + h * 3600;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/node/features/video/bilibili.ts
|
|
250
|
-
var BILIBILI_LINK = "https://player.bilibili.com/player.html";
|
|
251
|
-
var bilibiliPlugin = (md) => {
|
|
374
|
+
// src/node/features/codeSandbox.ts
|
|
375
|
+
var codeSandboxPlugin = (md) => {
|
|
252
376
|
createRuleBlock(md, {
|
|
253
|
-
type: "
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
syntaxPattern: /^@\[bilibili(?:\s+p(\d+))?([^\]]*)\]\(([^)]*)\)/,
|
|
257
|
-
meta([, page, info = "", source = ""]) {
|
|
377
|
+
type: "codesandbox",
|
|
378
|
+
syntaxPattern: /^@\[codesandbox(?:\s+(embed|button))?([^\]]*)\]\(([^)]*)\)/,
|
|
379
|
+
meta([, type2, info = "", source = ""]) {
|
|
258
380
|
const { attrs } = resolveAttrs(info);
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
const [aid, cid] = ids.filter((id) => !id.startsWith("BV"));
|
|
381
|
+
const [profile, filepath2 = ""] = source.split("#");
|
|
382
|
+
const [user, id] = profile.includes("/") ? profile.split("/") : ["", profile];
|
|
262
383
|
return {
|
|
263
|
-
page: +page || 1,
|
|
264
|
-
bvid,
|
|
265
|
-
aid,
|
|
266
|
-
cid,
|
|
267
|
-
autoplay: attrs.autoplay ?? false,
|
|
268
|
-
time: timeToSeconds(attrs.time),
|
|
269
|
-
title: attrs.title,
|
|
270
384
|
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
271
|
-
height: attrs.height ? parseRect(attrs.height) : "",
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (meta.aid) {
|
|
281
|
-
params.set("aid", meta.aid);
|
|
282
|
-
}
|
|
283
|
-
if (meta.cid) {
|
|
284
|
-
params.set("cid", meta.cid);
|
|
285
|
-
}
|
|
286
|
-
if (meta.page) {
|
|
287
|
-
params.set("p", meta.page.toString());
|
|
288
|
-
}
|
|
289
|
-
if (meta.time) {
|
|
290
|
-
params.set("t", meta.time.toString());
|
|
291
|
-
}
|
|
292
|
-
params.set("autoplay", meta.autoplay ? "1" : "0");
|
|
293
|
-
const source = `${BILIBILI_LINK}?${params.toString()}`;
|
|
294
|
-
return `<VideoBilibili src="${source}" width="${meta.width}" height="${meta.height}" ratio="${meta.ratio}" title="${meta.title}" />`;
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
// src/node/features/video/youtube.ts
|
|
300
|
-
import { URLSearchParams as URLSearchParams3 } from "node:url";
|
|
301
|
-
var YOUTUBE_LINK = "https://www.youtube.com/embed/";
|
|
302
|
-
var youtubePlugin = (md) => {
|
|
303
|
-
createRuleBlock(md, {
|
|
304
|
-
type: "youtube",
|
|
305
|
-
name: "video_youtube",
|
|
306
|
-
syntaxPattern: /^@\[youtube([^\]]*)\]\(([^)]*)\)/,
|
|
307
|
-
meta([, info = "", id = ""]) {
|
|
308
|
-
const { attrs } = resolveAttrs(info);
|
|
309
|
-
return {
|
|
310
|
-
id,
|
|
311
|
-
autoplay: attrs.autoplay ?? false,
|
|
312
|
-
loop: attrs.loop ?? false,
|
|
313
|
-
start: timeToSeconds(attrs.start),
|
|
314
|
-
end: timeToSeconds(attrs.end),
|
|
315
|
-
title: attrs.title,
|
|
316
|
-
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
317
|
-
height: attrs.height ? parseRect(attrs.height) : "",
|
|
318
|
-
ratio: attrs.ratio ? parseRect(attrs.ratio) : ""
|
|
319
|
-
};
|
|
320
|
-
},
|
|
321
|
-
content(meta) {
|
|
322
|
-
const params = new URLSearchParams3();
|
|
323
|
-
if (meta.autoplay) {
|
|
324
|
-
params.set("autoplay", "1");
|
|
325
|
-
}
|
|
326
|
-
if (meta.loop) {
|
|
327
|
-
params.set("loop", "1");
|
|
328
|
-
}
|
|
329
|
-
if (meta.start) {
|
|
330
|
-
params.set("start", meta.start.toString());
|
|
331
|
-
}
|
|
332
|
-
if (meta.end) {
|
|
333
|
-
params.set("end", meta.end.toString());
|
|
334
|
-
}
|
|
335
|
-
const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}`;
|
|
336
|
-
return `<VideoYoutube src="${source}" width="${meta.width}" height="${meta.height}" ratio="${meta.ratio}" title="${meta.title}" />`;
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
// src/node/features/codepen.ts
|
|
342
|
-
var CODEPEN_LINK = "https://codepen.io/";
|
|
343
|
-
var codepenPlugin = (md) => {
|
|
344
|
-
createRuleBlock(md, {
|
|
345
|
-
type: "codepen",
|
|
346
|
-
syntaxPattern: /^@\[codepen([^\]]*)\]\(([^)]*)\)/,
|
|
347
|
-
meta: ([, info = "", source = ""]) => {
|
|
348
|
-
const { attrs } = resolveAttrs(info);
|
|
349
|
-
const [user, slash] = source.split("/");
|
|
350
|
-
return {
|
|
351
|
-
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
352
|
-
height: attrs.height ? parseRect(attrs.height) : "400px",
|
|
353
|
-
user,
|
|
354
|
-
slash,
|
|
355
|
-
title: attrs.title,
|
|
356
|
-
preview: attrs.preview,
|
|
357
|
-
editable: attrs.editable,
|
|
358
|
-
tab: attrs.tab ?? "result",
|
|
359
|
-
theme: attrs.theme
|
|
360
|
-
};
|
|
361
|
-
},
|
|
362
|
-
content: (meta) => {
|
|
363
|
-
const { title = "Codepen", height, width } = meta;
|
|
364
|
-
const params = new URLSearchParams();
|
|
365
|
-
if (meta.editable) {
|
|
366
|
-
params.set("editable", "true");
|
|
367
|
-
}
|
|
368
|
-
if (meta.tab) {
|
|
369
|
-
params.set("default-tab", meta.tab);
|
|
370
|
-
}
|
|
371
|
-
if (meta.theme) {
|
|
372
|
-
params.set("theme-id", meta.theme);
|
|
373
|
-
}
|
|
374
|
-
const middle = meta.preview ? "/embed/preview/" : "/embed/";
|
|
375
|
-
const link = `${CODEPEN_LINK}${meta.user}${middle}${meta.slash}?${params.toString()}`;
|
|
376
|
-
const style = `width:${width};height:${height};margin:16px auto;border-radius:5px;`;
|
|
377
|
-
return `<iframe class="code-pen-iframe-wrapper" src="${link}" title="${title}" style="${style}" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">See the Pen <a href="${CODEPEN_LINK}${meta.user}/pen/${meta.slash}">${title}</a> by ${meta.user} (<a href="${CODEPEN_LINK}${meta.user}">@${meta.user}</a>) on <a href="${CODEPEN_LINK}">CodePen</a>.</iframe>`;
|
|
378
|
-
}
|
|
379
|
-
});
|
|
380
|
-
};
|
|
381
|
-
|
|
382
|
-
// src/node/features/replit.ts
|
|
383
|
-
var replitPlugin = (md) => {
|
|
384
|
-
createRuleBlock(md, {
|
|
385
|
-
type: "replit",
|
|
386
|
-
syntaxPattern: /^@\[replit([^\]]*)\]\(([^)]*)\)/,
|
|
387
|
-
meta: ([, info = "", source = ""]) => {
|
|
388
|
-
const { attrs } = resolveAttrs(info);
|
|
389
|
-
return {
|
|
390
|
-
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
391
|
-
height: attrs.height ? parseRect(attrs.height) : "450px",
|
|
392
|
-
source: source.startsWith("@") ? source : `@${source}`,
|
|
393
|
-
title: attrs.title,
|
|
394
|
-
theme: attrs.theme || ""
|
|
395
|
-
};
|
|
396
|
-
},
|
|
397
|
-
content({ title, height, width, source, theme }) {
|
|
398
|
-
return `<ReplitViewer title="${title || ""}" height="${height}" width="${width}" source="${source}" theme="${theme}" />`;
|
|
399
|
-
}
|
|
400
|
-
});
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
// src/node/features/codeSandbox.ts
|
|
404
|
-
var codeSandboxPlugin = (md) => {
|
|
405
|
-
createRuleBlock(md, {
|
|
406
|
-
type: "codesandbox",
|
|
407
|
-
syntaxPattern: /^@\[codesandbox(?:\s+(embed|button))?([^\]]*)\]\(([^)]*)\)/,
|
|
408
|
-
meta([, type2, info = "", source = ""]) {
|
|
409
|
-
const { attrs } = resolveAttrs(info);
|
|
410
|
-
const [profile, filepath2 = ""] = source.split("#");
|
|
411
|
-
const [user, id] = profile.includes("/") ? profile.split("/") : ["", profile];
|
|
412
|
-
return {
|
|
413
|
-
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
414
|
-
height: attrs.height ? parseRect(attrs.height) : "500px",
|
|
415
|
-
user,
|
|
416
|
-
id,
|
|
417
|
-
title: attrs.title ?? "",
|
|
418
|
-
console: attrs.console ?? false,
|
|
419
|
-
navbar: attrs.navbar ?? true,
|
|
420
|
-
layout: attrs.layout ?? "",
|
|
421
|
-
type: type2 || "embed",
|
|
422
|
-
filepath: filepath2
|
|
385
|
+
height: attrs.height ? parseRect(attrs.height) : "500px",
|
|
386
|
+
user,
|
|
387
|
+
id,
|
|
388
|
+
title: attrs.title ?? "",
|
|
389
|
+
console: attrs.console ?? false,
|
|
390
|
+
navbar: attrs.navbar ?? true,
|
|
391
|
+
layout: attrs.layout ?? "",
|
|
392
|
+
type: type2 || "embed",
|
|
393
|
+
filepath: filepath2
|
|
423
394
|
};
|
|
424
395
|
},
|
|
425
396
|
content({ title, height, width, user, id, type: type2, filepath: filepath2, console, navbar, layout }) {
|
|
@@ -428,302 +399,9 @@ var codeSandboxPlugin = (md) => {
|
|
|
428
399
|
});
|
|
429
400
|
};
|
|
430
401
|
|
|
431
|
-
// src/node/features/
|
|
432
|
-
|
|
433
|
-
createRuleBlock(md, {
|
|
434
|
-
type: "jsfiddle",
|
|
435
|
-
syntaxPattern: /^@\[jsfiddle([^\]]*)\]\(([^)]*)\)/,
|
|
436
|
-
meta([, info = "", source]) {
|
|
437
|
-
const { attrs } = resolveAttrs(info);
|
|
438
|
-
const [user, id] = source.split("/");
|
|
439
|
-
return {
|
|
440
|
-
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
441
|
-
height: attrs.height ? parseRect(attrs.height) : "400px",
|
|
442
|
-
user,
|
|
443
|
-
id,
|
|
444
|
-
title: attrs.title || "JS Fiddle",
|
|
445
|
-
tab: attrs.tab?.replace(/\s+/g, "") || "js,css,html,result",
|
|
446
|
-
theme: attrs.theme || "dark"
|
|
447
|
-
};
|
|
448
|
-
},
|
|
449
|
-
content: ({ title = "JS Fiddle", height, width, user, id, tab, theme }) => {
|
|
450
|
-
theme = theme === "dark" ? "/dark/" : "";
|
|
451
|
-
const link = `https://jsfiddle.net/${user}/${id}/embedded/${tab}${theme}`;
|
|
452
|
-
const style = `width:${width};height:${height};margin:16px auto;border:none;border-radius:5px;`;
|
|
453
|
-
return `<iframe class="js-fiddle-iframe-wrapper" style="${style}" title="${title}" src="${link}" allowfullscreen="true" allowpaymentrequest="true"></iframe>`;
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
// src/node/features/plot.ts
|
|
459
|
-
var [openTag2, endTag2] = ["!!", "!!"];
|
|
460
|
-
function createTokenizer2() {
|
|
461
|
-
return (state, silent) => {
|
|
462
|
-
let found = false;
|
|
463
|
-
const max = state.posMax;
|
|
464
|
-
const start = state.pos;
|
|
465
|
-
if (state.src.slice(start, start + 2) !== openTag2)
|
|
466
|
-
return false;
|
|
467
|
-
if (silent)
|
|
468
|
-
return false;
|
|
469
|
-
if (max - start < 5)
|
|
470
|
-
return false;
|
|
471
|
-
state.pos = start + 2;
|
|
472
|
-
while (state.pos < max) {
|
|
473
|
-
if (state.src.slice(state.pos - 1, state.pos + 1) === endTag2) {
|
|
474
|
-
found = true;
|
|
475
|
-
break;
|
|
476
|
-
}
|
|
477
|
-
state.md.inline.skipToken(state);
|
|
478
|
-
}
|
|
479
|
-
if (!found || start + 2 === state.pos) {
|
|
480
|
-
state.pos = start;
|
|
481
|
-
return false;
|
|
482
|
-
}
|
|
483
|
-
const content = state.src.slice(start + 2, state.pos - 1);
|
|
484
|
-
if (/^\s|\s$/.test(content)) {
|
|
485
|
-
state.pos = start;
|
|
486
|
-
return false;
|
|
487
|
-
}
|
|
488
|
-
state.posMax = state.pos - 1;
|
|
489
|
-
state.pos = start + 2;
|
|
490
|
-
const open = state.push("plot_open", "Plot", 1);
|
|
491
|
-
open.markup = openTag2;
|
|
492
|
-
const text = state.push("text", "", 0);
|
|
493
|
-
text.content = content;
|
|
494
|
-
const close = state.push("plot_close", "Plot", -1);
|
|
495
|
-
close.markup = endTag2;
|
|
496
|
-
state.pos = state.posMax + 2;
|
|
497
|
-
state.posMax = max;
|
|
498
|
-
return true;
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
var plotPlugin = (md) => {
|
|
502
|
-
md.inline.ruler.before("emphasis", "plot", createTokenizer2());
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
// src/node/features/langRepl.ts
|
|
402
|
+
// src/node/features/fileTree/index.ts
|
|
403
|
+
import fs2 from "node:fs";
|
|
506
404
|
import container2 from "markdown-it-container";
|
|
507
|
-
import { fs, getDirname, path as path2 } from "vuepress/utils";
|
|
508
|
-
var RE_INFO = /^(#editable)?(.*)$/;
|
|
509
|
-
function createReplContainer(md, lang) {
|
|
510
|
-
const type2 = `${lang}-repl`;
|
|
511
|
-
const validate = (info) => info.trim().startsWith(type2);
|
|
512
|
-
const render = (tokens, index) => {
|
|
513
|
-
const token = tokens[index];
|
|
514
|
-
const info = token.info.trim().slice(type2.length).trim() || "";
|
|
515
|
-
const [, editable, title] = info.match(RE_INFO) ?? [];
|
|
516
|
-
if (token.nesting === 1)
|
|
517
|
-
return `<CodeRepl ${editable ? "editable" : ""} title="${title || `${lang} playground`}">`;
|
|
518
|
-
else
|
|
519
|
-
return "</CodeRepl>";
|
|
520
|
-
};
|
|
521
|
-
md.use(container2, type2, { validate, render });
|
|
522
|
-
}
|
|
523
|
-
async function langReplPlugin(app, md, {
|
|
524
|
-
theme,
|
|
525
|
-
go = false,
|
|
526
|
-
kotlin = false,
|
|
527
|
-
rust = false
|
|
528
|
-
}) {
|
|
529
|
-
if (kotlin) {
|
|
530
|
-
createReplContainer(md, "kotlin");
|
|
531
|
-
}
|
|
532
|
-
if (go) {
|
|
533
|
-
createReplContainer(md, "go");
|
|
534
|
-
}
|
|
535
|
-
if (rust) {
|
|
536
|
-
createReplContainer(md, "rust");
|
|
537
|
-
}
|
|
538
|
-
theme ??= { light: "github-light", dark: "github-dark" };
|
|
539
|
-
const data = { grammars: {} };
|
|
540
|
-
const themesPath = getDirname(import.meta.resolve("tm-themes"));
|
|
541
|
-
const grammarsPath = getDirname(import.meta.resolve("tm-grammars"));
|
|
542
|
-
const readTheme = (theme2) => read(path2.join(themesPath, "themes", `${theme2}.json`));
|
|
543
|
-
const readGrammar = (grammar) => read(path2.join(grammarsPath, "grammars", `${grammar}.json`));
|
|
544
|
-
if (typeof theme === "string") {
|
|
545
|
-
data.theme = await readTheme(theme);
|
|
546
|
-
} else {
|
|
547
|
-
data.theme = await Promise.all([
|
|
548
|
-
readTheme(theme.light),
|
|
549
|
-
readTheme(theme.dark)
|
|
550
|
-
]).then(([light, dark]) => ({ light, dark }));
|
|
551
|
-
}
|
|
552
|
-
if (kotlin)
|
|
553
|
-
data.grammars.kotlin = await readGrammar("kotlin");
|
|
554
|
-
if (go)
|
|
555
|
-
data.grammars.go = await readGrammar("go");
|
|
556
|
-
if (rust)
|
|
557
|
-
data.grammars.rust = await readGrammar("rust");
|
|
558
|
-
await app.writeTemp(
|
|
559
|
-
"internal/md-power/replEditorData.js",
|
|
560
|
-
`export default ${JSON.stringify(data, null, 2)}`
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
async function read(file) {
|
|
564
|
-
try {
|
|
565
|
-
const content = await fs.readFile(file, "utf-8");
|
|
566
|
-
return JSON.parse(content);
|
|
567
|
-
} catch {
|
|
568
|
-
}
|
|
569
|
-
return void 0;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// src/node/prepareConfigFile.ts
|
|
573
|
-
import { getDirname as getDirname2, path as path3 } from "vuepress/utils";
|
|
574
|
-
import { ensureEndingSlash } from "@vuepress/helper";
|
|
575
|
-
var { url: filepath } = import.meta;
|
|
576
|
-
var __dirname = getDirname2(filepath);
|
|
577
|
-
var CLIENT_FOLDER = ensureEndingSlash(
|
|
578
|
-
path3.resolve(__dirname, "../client")
|
|
579
|
-
);
|
|
580
|
-
async function prepareConfigFile(app, options) {
|
|
581
|
-
const imports = /* @__PURE__ */ new Set();
|
|
582
|
-
const enhances = /* @__PURE__ */ new Set();
|
|
583
|
-
if (options.pdf) {
|
|
584
|
-
imports.add(`import PDFViewer from '${CLIENT_FOLDER}components/PDFViewer.vue'`);
|
|
585
|
-
enhances.add(`app.component('PDFViewer', PDFViewer)`);
|
|
586
|
-
}
|
|
587
|
-
if (options.bilibili) {
|
|
588
|
-
imports.add(`import Bilibili from '${CLIENT_FOLDER}components/Bilibili.vue'`);
|
|
589
|
-
enhances.add(`app.component('VideoBilibili', Bilibili)`);
|
|
590
|
-
}
|
|
591
|
-
if (options.youtube) {
|
|
592
|
-
imports.add(`import Youtube from '${CLIENT_FOLDER}components/Youtube.vue'`);
|
|
593
|
-
enhances.add(`app.component('VideoYoutube', Youtube)`);
|
|
594
|
-
}
|
|
595
|
-
if (options.replit) {
|
|
596
|
-
imports.add(`import Replit from '${CLIENT_FOLDER}components/Replit.vue'`);
|
|
597
|
-
enhances.add(`app.component('ReplitViewer', Replit)`);
|
|
598
|
-
}
|
|
599
|
-
if (options.codeSandbox) {
|
|
600
|
-
imports.add(`import CodeSandbox from '${CLIENT_FOLDER}components/CodeSandbox.vue'`);
|
|
601
|
-
enhances.add(`app.component('CodeSandboxViewer', CodeSandbox)`);
|
|
602
|
-
}
|
|
603
|
-
if (options.plot) {
|
|
604
|
-
imports.add(`import Plot from '${CLIENT_FOLDER}components/Plot.vue'`);
|
|
605
|
-
enhances.add(`app.component('Plot', Plot)`);
|
|
606
|
-
}
|
|
607
|
-
if (options.repl) {
|
|
608
|
-
imports.add(`import CodeRepl from '${CLIENT_FOLDER}components/CodeRepl.vue'`);
|
|
609
|
-
enhances.add(`app.component('CodeRepl', CodeRepl)`);
|
|
610
|
-
}
|
|
611
|
-
if (options.caniuse) {
|
|
612
|
-
imports.add(`import CanIUse from '${CLIENT_FOLDER}components/CanIUse.vue'`);
|
|
613
|
-
enhances.add(`app.component('CanIUseViewer', CanIUse)`);
|
|
614
|
-
}
|
|
615
|
-
if (options.fileTree) {
|
|
616
|
-
imports.add(`import FileTreeItem from '${CLIENT_FOLDER}components/FileTreeItem.vue'`);
|
|
617
|
-
imports.add(`import '@internal/md-power/file-tree.css'`);
|
|
618
|
-
enhances.add(`app.component('FileTreeItem', FileTreeItem)`);
|
|
619
|
-
}
|
|
620
|
-
return app.writeTemp(
|
|
621
|
-
"md-power/config.js",
|
|
622
|
-
`import { defineClientConfig } from 'vuepress/client'
|
|
623
|
-
${Array.from(imports.values()).join("\n")}
|
|
624
|
-
|
|
625
|
-
export default defineClientConfig({
|
|
626
|
-
enhance({ router, app }) {
|
|
627
|
-
${Array.from(enhances.values()).map((item) => ` ${item}`).join("\n")}
|
|
628
|
-
}
|
|
629
|
-
})
|
|
630
|
-
`
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
// src/node/features/fileTree/index.ts
|
|
635
|
-
import fs2 from "node:fs";
|
|
636
|
-
import container3 from "markdown-it-container";
|
|
637
|
-
|
|
638
|
-
// src/node/features/fileTree/resolveTreeNodeInfo.ts
|
|
639
|
-
import { removeLeadingSlash } from "vuepress/shared";
|
|
640
|
-
import Token from "markdown-it/lib/token.mjs";
|
|
641
|
-
function resolveTreeNodeInfo(tokens, current, idx) {
|
|
642
|
-
let hasInline = false;
|
|
643
|
-
let hasChildren = false;
|
|
644
|
-
let inline;
|
|
645
|
-
for (let i = idx + 1; !(tokens[i].level === current.level && tokens[i].type === "list_item_close"); ++i) {
|
|
646
|
-
if (tokens[i].type === "inline" && !hasInline) {
|
|
647
|
-
inline = tokens[i];
|
|
648
|
-
hasInline = true;
|
|
649
|
-
} else if (tokens[i].tag === "ul") {
|
|
650
|
-
hasChildren = true;
|
|
651
|
-
}
|
|
652
|
-
if (hasInline && hasChildren)
|
|
653
|
-
break;
|
|
654
|
-
}
|
|
655
|
-
if (!hasInline)
|
|
656
|
-
return void 0;
|
|
657
|
-
const children = inline.children?.filter((token) => token.type === "text" && token.content || token.tag === "strong") || [];
|
|
658
|
-
const filename = children.filter((token) => token.type === "text").map((token) => token.content).join(" ").split(/\s+/)[0] ?? "";
|
|
659
|
-
const focus = children[0]?.tag === "strong";
|
|
660
|
-
const type2 = hasChildren || filename.endsWith("/") ? "folder" : "file";
|
|
661
|
-
const info = {
|
|
662
|
-
filename: removeLeadingSlash(filename),
|
|
663
|
-
type: type2,
|
|
664
|
-
focus,
|
|
665
|
-
empty: !hasChildren,
|
|
666
|
-
expanded: type2 === "folder" && !filename.endsWith("/")
|
|
667
|
-
};
|
|
668
|
-
return [info, inline];
|
|
669
|
-
}
|
|
670
|
-
function updateInlineToken(inline, info, icon) {
|
|
671
|
-
const children = inline.children;
|
|
672
|
-
if (!children)
|
|
673
|
-
return;
|
|
674
|
-
const tokens = [];
|
|
675
|
-
const wrapperOpen = new Token("span_open", "span", 1);
|
|
676
|
-
const wrapperClose = new Token("span_close", "span", -1);
|
|
677
|
-
wrapperOpen.attrSet("class", `tree-node ${info.type}`);
|
|
678
|
-
tokens.push(wrapperOpen);
|
|
679
|
-
if (info.filename !== "..." && info.filename !== "\u2026") {
|
|
680
|
-
const iconOpen = new Token("span_open", "span", 1);
|
|
681
|
-
iconOpen.attrSet("class", icon);
|
|
682
|
-
const iconClose = new Token("span_close", "span", -1);
|
|
683
|
-
tokens.push(iconOpen, iconClose);
|
|
684
|
-
}
|
|
685
|
-
const fileOpen = new Token("span_open", "span", 1);
|
|
686
|
-
fileOpen.attrSet("class", `name${info.focus ? " focus" : ""}`);
|
|
687
|
-
tokens.push(fileOpen);
|
|
688
|
-
let isStrongTag = false;
|
|
689
|
-
while (children.length) {
|
|
690
|
-
const token = children.shift();
|
|
691
|
-
if (token.type === "text" && token.content) {
|
|
692
|
-
if (token.content.includes(" ")) {
|
|
693
|
-
const [first, ...other] = token.content.split(" ");
|
|
694
|
-
const text = new Token("text", "", 0);
|
|
695
|
-
text.content = first;
|
|
696
|
-
tokens.push(text);
|
|
697
|
-
const comment = new Token("text", "", 0);
|
|
698
|
-
comment.content = other.join(" ");
|
|
699
|
-
children.unshift(comment);
|
|
700
|
-
} else {
|
|
701
|
-
tokens.push(token);
|
|
702
|
-
}
|
|
703
|
-
if (!isStrongTag)
|
|
704
|
-
break;
|
|
705
|
-
} else if (token.tag === "strong") {
|
|
706
|
-
tokens.push(token);
|
|
707
|
-
if (token.nesting === 1) {
|
|
708
|
-
isStrongTag = true;
|
|
709
|
-
} else {
|
|
710
|
-
break;
|
|
711
|
-
}
|
|
712
|
-
} else {
|
|
713
|
-
tokens.push(token);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
const fileClose = new Token("span_close", "span", -1);
|
|
717
|
-
tokens.push(fileClose);
|
|
718
|
-
if (children.filter((token) => token.type === "text" && token.content.trim()).length) {
|
|
719
|
-
const commentOpen = new Token("span_open", "span", 1);
|
|
720
|
-
commentOpen.attrSet("class", "comment");
|
|
721
|
-
const commentClose = new Token("span_close", "span", -1);
|
|
722
|
-
tokens.push(commentOpen, ...children, commentClose);
|
|
723
|
-
}
|
|
724
|
-
tokens.push(wrapperClose);
|
|
725
|
-
inline.children = tokens;
|
|
726
|
-
}
|
|
727
405
|
|
|
728
406
|
// src/node/features/fileTree/icons.ts
|
|
729
407
|
var definitions = {
|
|
@@ -1323,118 +1001,618 @@ var FileIcons = {
|
|
|
1323
1001
|
"seti:ignored": '<path d="M4.661 20.860L21.083 4.480Q21.335 3.934 21.335 3.682L21.335 3.682Q21.167 2.968 20.600 2.779Q20.033 2.590 19.361 3.010L19.361 3.010L16.715 5.656Q16.589 5.782 16.211 5.782L16.211 5.782Q13.943 4.900 11.738 4.921Q9.533 4.942 7.307 5.908L7.307 5.908Q3.191 7.714 0.335 12.082L0.335 12.082Q0.125 12.502 0.146 12.817Q0.167 13.132 0.461 13.384L0.461 13.384Q1.469 14.602 2.687 15.610L2.687 15.610Q3.485 16.282 5.039 17.332L5.039 17.332Q4.997 17.332 4.934 17.395Q4.871 17.458 4.787 17.458L4.787 17.458L4.115 18.088Q3.317 18.886 2.939 19.306L2.939 19.306Q2.519 19.768 2.687 20.482L2.687 20.482Q2.897 21.112 3.611 21.280L3.611 21.280Q4.283 21.280 4.661 20.860L4.661 20.860ZM7.811 14.560L6.887 15.484L6.635 15.484Q4.241 14.182 2.687 12.502L2.687 12.502Q4.955 9.520 7.559 8.134L7.559 8.134Q5.627 11.410 7.811 14.560L7.811 14.560ZM12.809 8.302L12.809 8.302Q12.599 8.932 11.885 8.932L11.885 8.932Q11.003 8.932 10.373 9.478Q9.743 10.024 9.659 10.906L9.659 10.906L9.659 11.284Q9.659 11.662 9.428 11.872Q9.197 12.082 8.840 12.082Q8.483 12.082 8.273 11.830Q8.063 11.578 8.063 11.158L8.063 11.158Q8.063 9.562 9.197 8.407Q10.331 7.252 11.885 7.252L11.885 7.252Q12.347 7.252 12.620 7.567Q12.893 7.882 12.809 8.302ZM23.561 11.956L23.561 11.956Q23.225 11.578 22.574 10.717Q21.923 9.856 21.587 9.478L21.587 9.478Q21.335 9.142 20.684 8.554Q20.033 7.966 19.739 7.630L19.739 7.630L18.185 9.184Q20.033 10.654 21.335 12.502L21.335 12.502Q21.083 12.754 20.957 12.754L20.957 12.754Q20.159 13.510 19.739 13.804L19.739 13.804Q15.623 17.122 10.961 16.534L10.961 16.534Q10.583 16.534 10.415 16.702L10.415 16.702Q9.911 17.206 9.659 17.584L9.659 17.584L8.861 18.382L8.987 18.382Q12.137 19.054 14.615 18.508L14.615 18.508Q19.739 17.626 23.561 13.132L23.561 13.132Q24.149 12.754 23.561 11.956Z"/>'
|
|
1324
1002
|
};
|
|
1325
1003
|
|
|
1326
|
-
// src/node/features/fileTree/findIcon.ts
|
|
1327
|
-
var defaultFileIcon = {
|
|
1328
|
-
name: "default",
|
|
1329
|
-
svg: makeSVGIcon(FileIcons["seti:default"])
|
|
1004
|
+
// src/node/features/fileTree/findIcon.ts
|
|
1005
|
+
var defaultFileIcon = {
|
|
1006
|
+
name: "default",
|
|
1007
|
+
svg: makeSVGIcon(FileIcons["seti:default"])
|
|
1008
|
+
};
|
|
1009
|
+
var folderIcon = {
|
|
1010
|
+
name: "folder",
|
|
1011
|
+
svg: makeSVGIcon(FileIcons["seti:folder"])
|
|
1012
|
+
};
|
|
1013
|
+
function getFileIcon(fileName) {
|
|
1014
|
+
const name = getFileIconName(fileName);
|
|
1015
|
+
if (!name)
|
|
1016
|
+
return defaultFileIcon;
|
|
1017
|
+
if (name in FileIcons) {
|
|
1018
|
+
const path5 = FileIcons[name];
|
|
1019
|
+
return {
|
|
1020
|
+
name: name.includes(":") ? name.split(":")[1] : name,
|
|
1021
|
+
svg: makeSVGIcon(path5)
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
return defaultFileIcon;
|
|
1025
|
+
}
|
|
1026
|
+
function makeSVGIcon(svg) {
|
|
1027
|
+
svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">${svg}</svg>`.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/\{/g, "%7B").replace(/\}/g, "%7D").replace(/</g, "%3C").replace(/>/g, "%3E");
|
|
1028
|
+
return `url("data:image/svg+xml,${svg}")`;
|
|
1029
|
+
}
|
|
1030
|
+
function getFileIconName(fileName) {
|
|
1031
|
+
let icon = definitions.files[fileName];
|
|
1032
|
+
if (icon)
|
|
1033
|
+
return icon;
|
|
1034
|
+
icon = getFileIconTypeFromExtension(fileName);
|
|
1035
|
+
if (icon)
|
|
1036
|
+
return icon;
|
|
1037
|
+
for (const [partial, partialIcon] of Object.entries(definitions.partials)) {
|
|
1038
|
+
if (fileName.includes(partial))
|
|
1039
|
+
return partialIcon;
|
|
1040
|
+
}
|
|
1041
|
+
return icon;
|
|
1042
|
+
}
|
|
1043
|
+
function getFileIconTypeFromExtension(fileName) {
|
|
1044
|
+
const firstDotIndex = fileName.indexOf(".");
|
|
1045
|
+
if (firstDotIndex === -1)
|
|
1046
|
+
return;
|
|
1047
|
+
let extension = fileName.slice(firstDotIndex);
|
|
1048
|
+
while (extension !== "") {
|
|
1049
|
+
const icon = definitions.extensions[extension];
|
|
1050
|
+
if (icon)
|
|
1051
|
+
return icon;
|
|
1052
|
+
const nextDotIndex = extension.indexOf(".", 1);
|
|
1053
|
+
if (nextDotIndex === -1)
|
|
1054
|
+
return;
|
|
1055
|
+
extension = extension.slice(nextDotIndex);
|
|
1056
|
+
}
|
|
1057
|
+
return void 0;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// src/node/features/fileTree/resolveTreeNodeInfo.ts
|
|
1061
|
+
import Token from "markdown-it/lib/token.mjs";
|
|
1062
|
+
import { removeLeadingSlash } from "vuepress/shared";
|
|
1063
|
+
function resolveTreeNodeInfo(tokens, current, idx) {
|
|
1064
|
+
let hasInline = false;
|
|
1065
|
+
let hasChildren = false;
|
|
1066
|
+
let inline;
|
|
1067
|
+
for (let i = idx + 1; !(tokens[i].level === current.level && tokens[i].type === "list_item_close"); ++i) {
|
|
1068
|
+
if (tokens[i].type === "inline" && !hasInline) {
|
|
1069
|
+
inline = tokens[i];
|
|
1070
|
+
hasInline = true;
|
|
1071
|
+
} else if (tokens[i].tag === "ul") {
|
|
1072
|
+
hasChildren = true;
|
|
1073
|
+
}
|
|
1074
|
+
if (hasInline && hasChildren)
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
if (!hasInline)
|
|
1078
|
+
return void 0;
|
|
1079
|
+
const children = inline.children?.filter((token) => token.type === "text" && token.content || token.tag === "strong") || [];
|
|
1080
|
+
const filename = children.filter((token) => token.type === "text").map((token) => token.content).join(" ").split(/\s+/)[0] ?? "";
|
|
1081
|
+
const focus = children[0]?.tag === "strong";
|
|
1082
|
+
const type2 = hasChildren || filename.endsWith("/") ? "folder" : "file";
|
|
1083
|
+
const info = {
|
|
1084
|
+
filename: removeLeadingSlash(filename),
|
|
1085
|
+
type: type2,
|
|
1086
|
+
focus,
|
|
1087
|
+
empty: !hasChildren,
|
|
1088
|
+
expanded: type2 === "folder" && !filename.endsWith("/")
|
|
1089
|
+
};
|
|
1090
|
+
return [info, inline];
|
|
1091
|
+
}
|
|
1092
|
+
function updateInlineToken(inline, info, icon) {
|
|
1093
|
+
const children = inline.children;
|
|
1094
|
+
if (!children)
|
|
1095
|
+
return;
|
|
1096
|
+
const tokens = [];
|
|
1097
|
+
const wrapperOpen = new Token("span_open", "span", 1);
|
|
1098
|
+
const wrapperClose = new Token("span_close", "span", -1);
|
|
1099
|
+
wrapperOpen.attrSet("class", `tree-node ${info.type}`);
|
|
1100
|
+
tokens.push(wrapperOpen);
|
|
1101
|
+
if (info.filename !== "..." && info.filename !== "\u2026") {
|
|
1102
|
+
const iconOpen = new Token("span_open", "span", 1);
|
|
1103
|
+
iconOpen.attrSet("class", icon);
|
|
1104
|
+
const iconClose = new Token("span_close", "span", -1);
|
|
1105
|
+
tokens.push(iconOpen, iconClose);
|
|
1106
|
+
}
|
|
1107
|
+
const fileOpen = new Token("span_open", "span", 1);
|
|
1108
|
+
fileOpen.attrSet("class", `name${info.focus ? " focus" : ""}`);
|
|
1109
|
+
tokens.push(fileOpen);
|
|
1110
|
+
let isStrongTag = false;
|
|
1111
|
+
while (children.length) {
|
|
1112
|
+
const token = children.shift();
|
|
1113
|
+
if (token.type === "text" && token.content) {
|
|
1114
|
+
if (token.content.includes(" ")) {
|
|
1115
|
+
const [first, ...other] = token.content.split(" ");
|
|
1116
|
+
const text = new Token("text", "", 0);
|
|
1117
|
+
text.content = first;
|
|
1118
|
+
tokens.push(text);
|
|
1119
|
+
const comment = new Token("text", "", 0);
|
|
1120
|
+
comment.content = other.join(" ");
|
|
1121
|
+
children.unshift(comment);
|
|
1122
|
+
} else {
|
|
1123
|
+
tokens.push(token);
|
|
1124
|
+
}
|
|
1125
|
+
if (!isStrongTag)
|
|
1126
|
+
break;
|
|
1127
|
+
} else if (token.tag === "strong") {
|
|
1128
|
+
tokens.push(token);
|
|
1129
|
+
if (token.nesting === 1) {
|
|
1130
|
+
isStrongTag = true;
|
|
1131
|
+
} else {
|
|
1132
|
+
break;
|
|
1133
|
+
}
|
|
1134
|
+
} else {
|
|
1135
|
+
tokens.push(token);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
const fileClose = new Token("span_close", "span", -1);
|
|
1139
|
+
tokens.push(fileClose);
|
|
1140
|
+
if (children.filter((token) => token.type === "text" && token.content.trim()).length) {
|
|
1141
|
+
const commentOpen = new Token("span_open", "span", 1);
|
|
1142
|
+
commentOpen.attrSet("class", "comment");
|
|
1143
|
+
const commentClose = new Token("span_close", "span", -1);
|
|
1144
|
+
tokens.push(commentOpen, ...children, commentClose);
|
|
1145
|
+
}
|
|
1146
|
+
tokens.push(wrapperClose);
|
|
1147
|
+
inline.children = tokens;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/node/features/fileTree/index.ts
|
|
1151
|
+
var type = "file-tree";
|
|
1152
|
+
var closeType = `container_${type}_close`;
|
|
1153
|
+
var componentName = "FileTreeItem";
|
|
1154
|
+
var itemOpen = "file_tree_item_open";
|
|
1155
|
+
var itemClose = "file_tree_item_close";
|
|
1156
|
+
var classPrefix = "vp-fti-";
|
|
1157
|
+
var styleFilepath = "internal/md-power/file-tree.css";
|
|
1158
|
+
async function fileTreePlugin(app, md) {
|
|
1159
|
+
const validate = (info) => info.trim().startsWith(type);
|
|
1160
|
+
const render = (tokens, idx) => {
|
|
1161
|
+
if (tokens[idx].nesting === 1) {
|
|
1162
|
+
for (let i = idx + 1; !(tokens[i].nesting === -1 && tokens[i].type === closeType); ++i) {
|
|
1163
|
+
const token = tokens[i];
|
|
1164
|
+
if (token.type === "list_item_open") {
|
|
1165
|
+
const result = resolveTreeNodeInfo(tokens, token, i);
|
|
1166
|
+
if (result) {
|
|
1167
|
+
const [info2, inline] = result;
|
|
1168
|
+
const { filename, type: type2, expanded, empty } = info2;
|
|
1169
|
+
const icon = type2 === "file" ? getFileIcon(filename) : folderIcon;
|
|
1170
|
+
token.type = itemOpen;
|
|
1171
|
+
token.tag = componentName;
|
|
1172
|
+
token.attrSet("type", type2);
|
|
1173
|
+
token.attrSet(":expanded", expanded ? "true" : "false");
|
|
1174
|
+
token.attrSet(":empty", empty ? "true" : "false");
|
|
1175
|
+
updateInlineToken(inline, info2, `${classPrefix}${icon.name}`);
|
|
1176
|
+
addIcon(icon);
|
|
1177
|
+
}
|
|
1178
|
+
} else if (token.type === "list_item_close") {
|
|
1179
|
+
token.type = itemClose;
|
|
1180
|
+
token.tag = componentName;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const info = tokens[idx].info.trim();
|
|
1184
|
+
const title = info.slice(type.length).trim();
|
|
1185
|
+
return `<div class="vp-file-tree">${title ? `<p class="vp-file-tree-title">${title}</p>` : ""}`;
|
|
1186
|
+
} else {
|
|
1187
|
+
return "</div>";
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
let timer = null;
|
|
1191
|
+
const icons = {};
|
|
1192
|
+
function addIcon(icon) {
|
|
1193
|
+
icons[icon.name] = icon;
|
|
1194
|
+
if (timer)
|
|
1195
|
+
clearTimeout(timer);
|
|
1196
|
+
timer = setTimeout(async () => {
|
|
1197
|
+
let content = "";
|
|
1198
|
+
for (const icon2 of Object.values(icons)) {
|
|
1199
|
+
content += `.${classPrefix}${icon2.name} { --icon: ${icon2.svg}; }
|
|
1200
|
+
`;
|
|
1201
|
+
}
|
|
1202
|
+
await app.writeTemp(styleFilepath, content);
|
|
1203
|
+
}, 150);
|
|
1204
|
+
}
|
|
1205
|
+
md.use(container2, type, { validate, render });
|
|
1206
|
+
if (!fs2.existsSync(app.dir.temp(styleFilepath)))
|
|
1207
|
+
await app.writeTemp(styleFilepath, "");
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// src/node/features/icons.ts
|
|
1211
|
+
var [openTag, endTag] = [":[", "]:"];
|
|
1212
|
+
function createTokenizer() {
|
|
1213
|
+
return (state, silent) => {
|
|
1214
|
+
let found = false;
|
|
1215
|
+
const max = state.posMax;
|
|
1216
|
+
const start = state.pos;
|
|
1217
|
+
if (state.src.slice(start, start + 2) !== openTag)
|
|
1218
|
+
return false;
|
|
1219
|
+
if (silent)
|
|
1220
|
+
return false;
|
|
1221
|
+
if (max - start < 5)
|
|
1222
|
+
return false;
|
|
1223
|
+
state.pos = start + 2;
|
|
1224
|
+
while (state.pos < max) {
|
|
1225
|
+
if (state.src.slice(state.pos, state.pos + 2) === endTag) {
|
|
1226
|
+
found = true;
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
state.md.inline.skipToken(state);
|
|
1230
|
+
}
|
|
1231
|
+
if (!found || start + 2 === state.pos) {
|
|
1232
|
+
state.pos = start;
|
|
1233
|
+
return false;
|
|
1234
|
+
}
|
|
1235
|
+
const content = state.src.slice(start + 2, state.pos);
|
|
1236
|
+
if (/^\s|\s$/.test(content)) {
|
|
1237
|
+
state.pos = start;
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
state.posMax = state.pos;
|
|
1241
|
+
state.pos = start + 2;
|
|
1242
|
+
const [name, options = ""] = content.split(/\s+/);
|
|
1243
|
+
const [size, color] = options.split("/");
|
|
1244
|
+
const icon = state.push("vp_iconify_open", "VPIcon", 1);
|
|
1245
|
+
icon.markup = openTag;
|
|
1246
|
+
if (name)
|
|
1247
|
+
icon.attrSet("name", name);
|
|
1248
|
+
if (size)
|
|
1249
|
+
icon.attrSet("size", size);
|
|
1250
|
+
if (color)
|
|
1251
|
+
icon.attrSet("color", color);
|
|
1252
|
+
const close = state.push("vp_iconify_close", "VPIcon", -1);
|
|
1253
|
+
close.markup = endTag;
|
|
1254
|
+
state.pos = state.posMax + 2;
|
|
1255
|
+
state.posMax = max;
|
|
1256
|
+
return true;
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
var iconsPlugin = (md) => {
|
|
1260
|
+
md.inline.ruler.before("emphasis", "iconify", createTokenizer());
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
// src/node/features/jsfiddle.ts
|
|
1264
|
+
var jsfiddlePlugin = (md) => {
|
|
1265
|
+
createRuleBlock(md, {
|
|
1266
|
+
type: "jsfiddle",
|
|
1267
|
+
syntaxPattern: /^@\[jsfiddle([^\]]*)\]\(([^)]*)\)/,
|
|
1268
|
+
meta([, info = "", source]) {
|
|
1269
|
+
const { attrs } = resolveAttrs(info);
|
|
1270
|
+
const [user, id] = source.split("/");
|
|
1271
|
+
return {
|
|
1272
|
+
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
1273
|
+
height: attrs.height ? parseRect(attrs.height) : "400px",
|
|
1274
|
+
user,
|
|
1275
|
+
id,
|
|
1276
|
+
title: attrs.title || "JS Fiddle",
|
|
1277
|
+
tab: attrs.tab?.replace(/\s+/g, "") || "js,css,html,result",
|
|
1278
|
+
theme: attrs.theme || "dark"
|
|
1279
|
+
};
|
|
1280
|
+
},
|
|
1281
|
+
content: ({ title = "JS Fiddle", height, width, user, id, tab, theme }) => {
|
|
1282
|
+
theme = theme === "dark" ? "/dark/" : "";
|
|
1283
|
+
const link = `https://jsfiddle.net/${user}/${id}/embedded/${tab}${theme}`;
|
|
1284
|
+
const style = `width:${width};height:${height};margin:16px auto;border:none;border-radius:5px;`;
|
|
1285
|
+
return `<iframe class="js-fiddle-iframe-wrapper" style="${style}" title="${title}" src="${link}" allowfullscreen="true" allowpaymentrequest="true"></iframe>`;
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
// src/node/features/langRepl.ts
|
|
1291
|
+
import container3 from "markdown-it-container";
|
|
1292
|
+
import { fs as fs3, getDirname, path as path2 } from "vuepress/utils";
|
|
1293
|
+
var RE_INFO = /^(#editable)?(.*)$/;
|
|
1294
|
+
function createReplContainer(md, lang) {
|
|
1295
|
+
const type2 = `${lang}-repl`;
|
|
1296
|
+
const validate = (info) => info.trim().startsWith(type2);
|
|
1297
|
+
const render = (tokens, index) => {
|
|
1298
|
+
const token = tokens[index];
|
|
1299
|
+
const info = token.info.trim().slice(type2.length).trim() || "";
|
|
1300
|
+
const [, editable, title] = info.match(RE_INFO) ?? [];
|
|
1301
|
+
if (token.nesting === 1)
|
|
1302
|
+
return `<CodeRepl ${editable ? "editable" : ""} title="${title || `${lang} playground`}">`;
|
|
1303
|
+
else
|
|
1304
|
+
return "</CodeRepl>";
|
|
1305
|
+
};
|
|
1306
|
+
md.use(container3, type2, { validate, render });
|
|
1307
|
+
}
|
|
1308
|
+
async function langReplPlugin(app, md, {
|
|
1309
|
+
theme,
|
|
1310
|
+
go = false,
|
|
1311
|
+
kotlin = false,
|
|
1312
|
+
rust = false
|
|
1313
|
+
}) {
|
|
1314
|
+
if (kotlin) {
|
|
1315
|
+
createReplContainer(md, "kotlin");
|
|
1316
|
+
}
|
|
1317
|
+
if (go) {
|
|
1318
|
+
createReplContainer(md, "go");
|
|
1319
|
+
}
|
|
1320
|
+
if (rust) {
|
|
1321
|
+
createReplContainer(md, "rust");
|
|
1322
|
+
}
|
|
1323
|
+
theme ??= { light: "github-light", dark: "github-dark" };
|
|
1324
|
+
const data = { grammars: {} };
|
|
1325
|
+
const themesPath = getDirname(import.meta.resolve("tm-themes"));
|
|
1326
|
+
const grammarsPath = getDirname(import.meta.resolve("tm-grammars"));
|
|
1327
|
+
const readTheme = (theme2) => read(path2.join(themesPath, "themes", `${theme2}.json`));
|
|
1328
|
+
const readGrammar = (grammar) => read(path2.join(grammarsPath, "grammars", `${grammar}.json`));
|
|
1329
|
+
if (typeof theme === "string") {
|
|
1330
|
+
data.theme = await readTheme(theme);
|
|
1331
|
+
} else {
|
|
1332
|
+
data.theme = await Promise.all([
|
|
1333
|
+
readTheme(theme.light),
|
|
1334
|
+
readTheme(theme.dark)
|
|
1335
|
+
]).then(([light, dark]) => ({ light, dark }));
|
|
1336
|
+
}
|
|
1337
|
+
if (kotlin)
|
|
1338
|
+
data.grammars.kotlin = await readGrammar("kotlin");
|
|
1339
|
+
if (go)
|
|
1340
|
+
data.grammars.go = await readGrammar("go");
|
|
1341
|
+
if (rust)
|
|
1342
|
+
data.grammars.rust = await readGrammar("rust");
|
|
1343
|
+
await app.writeTemp(
|
|
1344
|
+
"internal/md-power/replEditorData.js",
|
|
1345
|
+
`export default ${JSON.stringify(data, null, 2)}`
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
async function read(file) {
|
|
1349
|
+
try {
|
|
1350
|
+
const content = await fs3.readFile(file, "utf-8");
|
|
1351
|
+
return JSON.parse(content);
|
|
1352
|
+
} catch {
|
|
1353
|
+
}
|
|
1354
|
+
return void 0;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// src/node/features/pdf.ts
|
|
1358
|
+
import { path as path3 } from "vuepress/utils";
|
|
1359
|
+
var pdfPlugin = (md) => {
|
|
1360
|
+
createRuleBlock(md, {
|
|
1361
|
+
type: "pdf",
|
|
1362
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
1363
|
+
syntaxPattern: /^@\[pdf(?:\s+(\d+))?([^\]]*)\]\(([^)]*)\)/,
|
|
1364
|
+
meta([, page, info = "", src = ""]) {
|
|
1365
|
+
const { attrs } = resolveAttrs(info);
|
|
1366
|
+
return {
|
|
1367
|
+
src,
|
|
1368
|
+
page: +page || 1,
|
|
1369
|
+
noToolbar: Boolean(attrs.noToolbar ?? false),
|
|
1370
|
+
zoom: +attrs.zoom || 50,
|
|
1371
|
+
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
1372
|
+
height: attrs.height ? parseRect(attrs.height) : "",
|
|
1373
|
+
ratio: attrs.ratio ? parseRect(attrs.ratio) : "",
|
|
1374
|
+
title: path3.basename(src || "")
|
|
1375
|
+
};
|
|
1376
|
+
},
|
|
1377
|
+
content({ title, src, page, noToolbar, width, height, ratio, zoom }) {
|
|
1378
|
+
return `<PDFViewer src="${src}" title="${title}" :page="${page}" :no-toolbar="${noToolbar}" width="${width}" height="${height}" ratio="${ratio}" :zoom="${zoom}" />`;
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// src/node/features/plot.ts
|
|
1384
|
+
var [openTag2, endTag2] = ["!!", "!!"];
|
|
1385
|
+
function createTokenizer2() {
|
|
1386
|
+
return (state, silent) => {
|
|
1387
|
+
let found = false;
|
|
1388
|
+
const max = state.posMax;
|
|
1389
|
+
const start = state.pos;
|
|
1390
|
+
if (state.src.slice(start, start + 2) !== openTag2)
|
|
1391
|
+
return false;
|
|
1392
|
+
if (silent)
|
|
1393
|
+
return false;
|
|
1394
|
+
if (max - start < 5)
|
|
1395
|
+
return false;
|
|
1396
|
+
state.pos = start + 2;
|
|
1397
|
+
while (state.pos < max) {
|
|
1398
|
+
if (state.src.slice(state.pos - 1, state.pos + 1) === endTag2) {
|
|
1399
|
+
found = true;
|
|
1400
|
+
break;
|
|
1401
|
+
}
|
|
1402
|
+
state.md.inline.skipToken(state);
|
|
1403
|
+
}
|
|
1404
|
+
if (!found || start + 2 === state.pos) {
|
|
1405
|
+
state.pos = start;
|
|
1406
|
+
return false;
|
|
1407
|
+
}
|
|
1408
|
+
const content = state.src.slice(start + 2, state.pos - 1);
|
|
1409
|
+
if (/^\s|\s$/.test(content)) {
|
|
1410
|
+
state.pos = start;
|
|
1411
|
+
return false;
|
|
1412
|
+
}
|
|
1413
|
+
state.posMax = state.pos - 1;
|
|
1414
|
+
state.pos = start + 2;
|
|
1415
|
+
const open = state.push("plot_open", "Plot", 1);
|
|
1416
|
+
open.markup = openTag2;
|
|
1417
|
+
const text = state.push("text", "", 0);
|
|
1418
|
+
text.content = content;
|
|
1419
|
+
const close = state.push("plot_close", "Plot", -1);
|
|
1420
|
+
close.markup = endTag2;
|
|
1421
|
+
state.pos = state.posMax + 2;
|
|
1422
|
+
state.posMax = max;
|
|
1423
|
+
return true;
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
var plotPlugin = (md) => {
|
|
1427
|
+
md.inline.ruler.before("emphasis", "plot", createTokenizer2());
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
// src/node/features/replit.ts
|
|
1431
|
+
var replitPlugin = (md) => {
|
|
1432
|
+
createRuleBlock(md, {
|
|
1433
|
+
type: "replit",
|
|
1434
|
+
syntaxPattern: /^@\[replit([^\]]*)\]\(([^)]*)\)/,
|
|
1435
|
+
meta: ([, info = "", source = ""]) => {
|
|
1436
|
+
const { attrs } = resolveAttrs(info);
|
|
1437
|
+
return {
|
|
1438
|
+
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
1439
|
+
height: attrs.height ? parseRect(attrs.height) : "450px",
|
|
1440
|
+
source: source.startsWith("@") ? source : `@${source}`,
|
|
1441
|
+
title: attrs.title,
|
|
1442
|
+
theme: attrs.theme || ""
|
|
1443
|
+
};
|
|
1444
|
+
},
|
|
1445
|
+
content({ title, height, width, source, theme }) {
|
|
1446
|
+
return `<ReplitViewer title="${title || ""}" height="${height}" width="${width}" source="${source}" theme="${theme}" />`;
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
};
|
|
1450
|
+
|
|
1451
|
+
// src/node/features/video/bilibili.ts
|
|
1452
|
+
import { URLSearchParams as URLSearchParams2 } from "node:url";
|
|
1453
|
+
|
|
1454
|
+
// src/node/utils/timeToSeconds.ts
|
|
1455
|
+
function timeToSeconds(time) {
|
|
1456
|
+
if (!time)
|
|
1457
|
+
return 0;
|
|
1458
|
+
if (Number.parseFloat(time) === Number(time))
|
|
1459
|
+
return Number(time);
|
|
1460
|
+
const [s, m, h] = time.split(":").reverse().map((n) => Number(n) || 0);
|
|
1461
|
+
return s + m * 60 + h * 3600;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// src/node/features/video/bilibili.ts
|
|
1465
|
+
var BILIBILI_LINK = "https://player.bilibili.com/player.html";
|
|
1466
|
+
var bilibiliPlugin = (md) => {
|
|
1467
|
+
createRuleBlock(md, {
|
|
1468
|
+
type: "bilibili",
|
|
1469
|
+
name: "video_bilibili",
|
|
1470
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
1471
|
+
syntaxPattern: /^@\[bilibili(?:\s+p(\d+))?([^\]]*)\]\(([^)]*)\)/,
|
|
1472
|
+
meta([, page, info = "", source = ""]) {
|
|
1473
|
+
const { attrs } = resolveAttrs(info);
|
|
1474
|
+
const ids = source.trim().split(/\s+/);
|
|
1475
|
+
const bvid = ids.find((id) => id.startsWith("BV"));
|
|
1476
|
+
const [aid, cid] = ids.filter((id) => !id.startsWith("BV"));
|
|
1477
|
+
return {
|
|
1478
|
+
page: +page || 1,
|
|
1479
|
+
bvid,
|
|
1480
|
+
aid,
|
|
1481
|
+
cid,
|
|
1482
|
+
autoplay: attrs.autoplay ?? false,
|
|
1483
|
+
time: timeToSeconds(attrs.time),
|
|
1484
|
+
title: attrs.title,
|
|
1485
|
+
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
1486
|
+
height: attrs.height ? parseRect(attrs.height) : "",
|
|
1487
|
+
ratio: attrs.ratio ? parseRect(attrs.ratio) : ""
|
|
1488
|
+
};
|
|
1489
|
+
},
|
|
1490
|
+
content(meta) {
|
|
1491
|
+
const params = new URLSearchParams2();
|
|
1492
|
+
if (meta.bvid) {
|
|
1493
|
+
params.set("bvid", meta.bvid);
|
|
1494
|
+
}
|
|
1495
|
+
if (meta.aid) {
|
|
1496
|
+
params.set("aid", meta.aid);
|
|
1497
|
+
}
|
|
1498
|
+
if (meta.cid) {
|
|
1499
|
+
params.set("cid", meta.cid);
|
|
1500
|
+
}
|
|
1501
|
+
if (meta.page) {
|
|
1502
|
+
params.set("p", meta.page.toString());
|
|
1503
|
+
}
|
|
1504
|
+
if (meta.time) {
|
|
1505
|
+
params.set("t", meta.time.toString());
|
|
1506
|
+
}
|
|
1507
|
+
params.set("autoplay", meta.autoplay ? "1" : "0");
|
|
1508
|
+
const source = `${BILIBILI_LINK}?${params.toString()}`;
|
|
1509
|
+
return `<VideoBilibili src="${source}" width="${meta.width}" height="${meta.height}" ratio="${meta.ratio}" title="${meta.title}" />`;
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1330
1512
|
};
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1513
|
+
|
|
1514
|
+
// src/node/features/video/youtube.ts
|
|
1515
|
+
import { URLSearchParams as URLSearchParams3 } from "node:url";
|
|
1516
|
+
var YOUTUBE_LINK = "https://www.youtube.com/embed/";
|
|
1517
|
+
var youtubePlugin = (md) => {
|
|
1518
|
+
createRuleBlock(md, {
|
|
1519
|
+
type: "youtube",
|
|
1520
|
+
name: "video_youtube",
|
|
1521
|
+
syntaxPattern: /^@\[youtube([^\]]*)\]\(([^)]*)\)/,
|
|
1522
|
+
meta([, info = "", id = ""]) {
|
|
1523
|
+
const { attrs } = resolveAttrs(info);
|
|
1524
|
+
return {
|
|
1525
|
+
id,
|
|
1526
|
+
autoplay: attrs.autoplay ?? false,
|
|
1527
|
+
loop: attrs.loop ?? false,
|
|
1528
|
+
start: timeToSeconds(attrs.start),
|
|
1529
|
+
end: timeToSeconds(attrs.end),
|
|
1530
|
+
title: attrs.title,
|
|
1531
|
+
width: attrs.width ? parseRect(attrs.width) : "100%",
|
|
1532
|
+
height: attrs.height ? parseRect(attrs.height) : "",
|
|
1533
|
+
ratio: attrs.ratio ? parseRect(attrs.ratio) : ""
|
|
1534
|
+
};
|
|
1535
|
+
},
|
|
1536
|
+
content(meta) {
|
|
1537
|
+
const params = new URLSearchParams3();
|
|
1538
|
+
if (meta.autoplay) {
|
|
1539
|
+
params.set("autoplay", "1");
|
|
1540
|
+
}
|
|
1541
|
+
if (meta.loop) {
|
|
1542
|
+
params.set("loop", "1");
|
|
1543
|
+
}
|
|
1544
|
+
if (meta.start) {
|
|
1545
|
+
params.set("start", meta.start.toString());
|
|
1546
|
+
}
|
|
1547
|
+
if (meta.end) {
|
|
1548
|
+
params.set("end", meta.end.toString());
|
|
1549
|
+
}
|
|
1550
|
+
const source = `${YOUTUBE_LINK}/${meta.id}?${params.toString()}`;
|
|
1551
|
+
return `<VideoYoutube src="${source}" width="${meta.width}" height="${meta.height}" ratio="${meta.ratio}" title="${meta.title}" />`;
|
|
1552
|
+
}
|
|
1553
|
+
});
|
|
1334
1554
|
};
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1555
|
+
|
|
1556
|
+
// src/node/prepareConfigFile.ts
|
|
1557
|
+
import { ensureEndingSlash } from "@vuepress/helper";
|
|
1558
|
+
import { getDirname as getDirname2, path as path4 } from "vuepress/utils";
|
|
1559
|
+
var { url: filepath } = import.meta;
|
|
1560
|
+
var __dirname = getDirname2(filepath);
|
|
1561
|
+
var CLIENT_FOLDER = ensureEndingSlash(
|
|
1562
|
+
path4.resolve(__dirname, "../client")
|
|
1563
|
+
);
|
|
1564
|
+
async function prepareConfigFile(app, options) {
|
|
1565
|
+
const imports = /* @__PURE__ */ new Set();
|
|
1566
|
+
const enhances = /* @__PURE__ */ new Set();
|
|
1567
|
+
if (options.pdf) {
|
|
1568
|
+
imports.add(`import PDFViewer from '${CLIENT_FOLDER}components/PDFViewer.vue'`);
|
|
1569
|
+
enhances.add(`app.component('PDFViewer', PDFViewer)`);
|
|
1345
1570
|
}
|
|
1346
|
-
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">${svg}</svg>`.replace(/"/g, "'").replace(/%/g, "%25").replace(/#/g, "%23").replace(/\{/g, "%7B").replace(/\}/g, "%7D").replace(/</g, "%3C").replace(/>/g, "%3E");
|
|
1350
|
-
return `url("data:image/svg+xml,${svg}")`;
|
|
1351
|
-
}
|
|
1352
|
-
function getFileIconName(fileName) {
|
|
1353
|
-
let icon = definitions.files[fileName];
|
|
1354
|
-
if (icon)
|
|
1355
|
-
return icon;
|
|
1356
|
-
icon = getFileIconTypeFromExtension(fileName);
|
|
1357
|
-
if (icon)
|
|
1358
|
-
return icon;
|
|
1359
|
-
for (const [partial, partialIcon] of Object.entries(definitions.partials)) {
|
|
1360
|
-
if (fileName.includes(partial))
|
|
1361
|
-
return partialIcon;
|
|
1571
|
+
if (options.bilibili) {
|
|
1572
|
+
imports.add(`import Bilibili from '${CLIENT_FOLDER}components/Bilibili.vue'`);
|
|
1573
|
+
enhances.add(`app.component('VideoBilibili', Bilibili)`);
|
|
1362
1574
|
}
|
|
1363
|
-
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
const firstDotIndex = fileName.indexOf(".");
|
|
1367
|
-
if (firstDotIndex === -1)
|
|
1368
|
-
return;
|
|
1369
|
-
let extension = fileName.slice(firstDotIndex);
|
|
1370
|
-
while (extension !== "") {
|
|
1371
|
-
const icon = definitions.extensions[extension];
|
|
1372
|
-
if (icon)
|
|
1373
|
-
return icon;
|
|
1374
|
-
const nextDotIndex = extension.indexOf(".", 1);
|
|
1375
|
-
if (nextDotIndex === -1)
|
|
1376
|
-
return;
|
|
1377
|
-
extension = extension.slice(nextDotIndex);
|
|
1575
|
+
if (options.youtube) {
|
|
1576
|
+
imports.add(`import Youtube from '${CLIENT_FOLDER}components/Youtube.vue'`);
|
|
1577
|
+
enhances.add(`app.component('VideoYoutube', Youtube)`);
|
|
1378
1578
|
}
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1579
|
+
if (options.replit) {
|
|
1580
|
+
imports.add(`import Replit from '${CLIENT_FOLDER}components/Replit.vue'`);
|
|
1581
|
+
enhances.add(`app.component('ReplitViewer', Replit)`);
|
|
1582
|
+
}
|
|
1583
|
+
if (options.codeSandbox) {
|
|
1584
|
+
imports.add(`import CodeSandbox from '${CLIENT_FOLDER}components/CodeSandbox.vue'`);
|
|
1585
|
+
enhances.add(`app.component('CodeSandboxViewer', CodeSandbox)`);
|
|
1586
|
+
}
|
|
1587
|
+
if (options.plot) {
|
|
1588
|
+
imports.add(`import Plot from '${CLIENT_FOLDER}components/Plot.vue'`);
|
|
1589
|
+
enhances.add(`app.component('Plot', Plot)`);
|
|
1590
|
+
}
|
|
1591
|
+
if (options.repl) {
|
|
1592
|
+
imports.add(`import CodeRepl from '${CLIENT_FOLDER}components/CodeRepl.vue'`);
|
|
1593
|
+
enhances.add(`app.component('CodeRepl', CodeRepl)`);
|
|
1594
|
+
}
|
|
1595
|
+
if (options.caniuse) {
|
|
1596
|
+
imports.add(`import CanIUse from '${CLIENT_FOLDER}components/CanIUse.vue'`);
|
|
1597
|
+
enhances.add(`app.component('CanIUseViewer', CanIUse)`);
|
|
1598
|
+
}
|
|
1599
|
+
if (options.fileTree) {
|
|
1600
|
+
imports.add(`import FileTreeItem from '${CLIENT_FOLDER}components/FileTreeItem.vue'`);
|
|
1601
|
+
imports.add(`import '@internal/md-power/file-tree.css'`);
|
|
1602
|
+
enhances.add(`app.component('FileTreeItem', FileTreeItem)`);
|
|
1603
|
+
}
|
|
1604
|
+
return app.writeTemp(
|
|
1605
|
+
"md-power/config.js",
|
|
1606
|
+
`import { defineClientConfig } from 'vuepress/client'
|
|
1607
|
+
${Array.from(imports.values()).join("\n")}
|
|
1381
1608
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
var componentName = "FileTreeItem";
|
|
1386
|
-
var itemOpen = "file_tree_item_open";
|
|
1387
|
-
var itemClose = "file_tree_item_close";
|
|
1388
|
-
var classPrefix = "vp-fti-";
|
|
1389
|
-
var styleFilepath = "internal/md-power/file-tree.css";
|
|
1390
|
-
async function fileTreePlugin(app, md) {
|
|
1391
|
-
const validate = (info) => info.trim().startsWith(type);
|
|
1392
|
-
const render = (tokens, idx) => {
|
|
1393
|
-
if (tokens[idx].nesting === 1) {
|
|
1394
|
-
for (let i = idx + 1; !(tokens[i].nesting === -1 && tokens[i].type === closeType); ++i) {
|
|
1395
|
-
const token = tokens[i];
|
|
1396
|
-
if (token.type === "list_item_open") {
|
|
1397
|
-
const result = resolveTreeNodeInfo(tokens, token, i);
|
|
1398
|
-
if (result) {
|
|
1399
|
-
const [info, inline] = result;
|
|
1400
|
-
const { filename, type: type2, expanded, empty } = info;
|
|
1401
|
-
const icon = type2 === "file" ? getFileIcon(filename) : folderIcon;
|
|
1402
|
-
token.type = itemOpen;
|
|
1403
|
-
token.tag = componentName;
|
|
1404
|
-
token.attrSet("type", type2);
|
|
1405
|
-
token.attrSet(":expanded", expanded ? "true" : "false");
|
|
1406
|
-
token.attrSet(":empty", empty ? "true" : "false");
|
|
1407
|
-
updateInlineToken(inline, info, `${classPrefix}${icon.name}`);
|
|
1408
|
-
addIcon(icon);
|
|
1409
|
-
}
|
|
1410
|
-
} else if (token.type === "list_item_close") {
|
|
1411
|
-
token.type = itemClose;
|
|
1412
|
-
token.tag = componentName;
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
return '<div class="vp-file-tree">';
|
|
1416
|
-
} else {
|
|
1417
|
-
return "</div>";
|
|
1418
|
-
}
|
|
1419
|
-
};
|
|
1420
|
-
let timer = null;
|
|
1421
|
-
const icons = {};
|
|
1422
|
-
function addIcon(icon) {
|
|
1423
|
-
icons[icon.name] = icon;
|
|
1424
|
-
if (timer)
|
|
1425
|
-
clearTimeout(timer);
|
|
1426
|
-
timer = setTimeout(async () => {
|
|
1427
|
-
let content = "";
|
|
1428
|
-
for (const icon2 of Object.values(icons)) {
|
|
1429
|
-
content += `.${classPrefix}${icon2.name} { --icon: ${icon2.svg}; }
|
|
1430
|
-
`;
|
|
1431
|
-
}
|
|
1432
|
-
await app.writeTemp(styleFilepath, content);
|
|
1433
|
-
}, 150);
|
|
1609
|
+
export default defineClientConfig({
|
|
1610
|
+
enhance({ router, app }) {
|
|
1611
|
+
${Array.from(enhances.values()).map((item) => ` ${item}`).join("\n")}
|
|
1434
1612
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1613
|
+
})
|
|
1614
|
+
`
|
|
1615
|
+
);
|
|
1438
1616
|
}
|
|
1439
1617
|
|
|
1440
1618
|
// src/node/plugin.ts
|
|
@@ -1456,6 +1634,7 @@ function markdownPowerPlugin(options = {}) {
|
|
|
1456
1634
|
}
|
|
1457
1635
|
},
|
|
1458
1636
|
extendsMarkdown: async (md, app2) => {
|
|
1637
|
+
await imageSizePlugin(app2, md, options.imageSize);
|
|
1459
1638
|
if (options.caniuse) {
|
|
1460
1639
|
const caniuse = options.caniuse === true ? {} : options.caniuse;
|
|
1461
1640
|
md.use(caniusePlugin, caniuse);
|
|
@@ -1498,5 +1677,6 @@ function markdownPowerPlugin(options = {}) {
|
|
|
1498
1677
|
};
|
|
1499
1678
|
}
|
|
1500
1679
|
export {
|
|
1501
|
-
markdownPowerPlugin
|
|
1680
|
+
markdownPowerPlugin,
|
|
1681
|
+
resolveImageSize
|
|
1502
1682
|
};
|