@slidev/cli 52.15.2 → 52.17.0
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/README.md +1 -1
- package/dist/{build-BZYW8gn1.mjs → build-Cszi5hDI.mjs} +5 -8
- package/dist/cli.mjs +37 -27
- package/dist/{export-C2XL3OpJ.mjs → export-CU2GL9dm.mjs} +21 -23
- package/dist/index.d.mts +1 -3
- package/dist/index.mjs +2 -5
- package/dist/{resolver-BhqywfGz.mjs → resolver-BBT539iz.mjs} +116 -25
- package/dist/{shared-CmTSrQGO.mjs → serve-CgOXLPUI.mjs} +920 -476
- package/dist/stdio-LsewwOTO.mjs +24 -0
- package/package.json +36 -33
- package/skills/slidev/SKILL.md +1 -0
- package/skills/slidev/references/core-cli.md +8 -0
- package/skills/slidev/references/tool-mcp.md +48 -0
- package/dist/serve-CLoYUI-M.mjs +0 -13
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { a as resolveImportPath, c as toAtFS, i as resolveEntry, n as getRoots, o as resolveImportUrl, r as isInstalledGlobally, s as resolveSourceFiles, t as createResolver } from "./resolver-
|
|
1
|
+
import { a as resolveImportPath, c as toAtFS, i as resolveEntry, n as getRoots, o as resolveImportUrl, r as isInstalledGlobally, s as resolveSourceFiles, t as createResolver } from "./resolver-BBT539iz.mjs";
|
|
2
2
|
import { builtinModules } from "node:module";
|
|
3
3
|
import path, { basename, dirname, join, relative, resolve, win32 } from "node:path";
|
|
4
|
-
import
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { createServer, loadConfigFromFile, mergeConfig, parseSync } from "vite";
|
|
5
6
|
import MarkdownExit from "markdown-exit";
|
|
6
|
-
import fs, { existsSync, realpathSync } from "node:fs";
|
|
7
|
+
import fs, { existsSync, mkdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
7
8
|
import fs$1, { readFile } from "node:fs/promises";
|
|
8
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
10
|
import { ensureSuffix, isString, isTruthy, notNullish, objectEntries, objectMap, range, slash, uniq } from "@antfu/utils";
|
|
10
11
|
import { bold, gray, red, white, yellow } from "ansis";
|
|
11
12
|
import { createResolve } from "mlly";
|
|
12
13
|
import { findDepPkgJsonPath } from "vitefu";
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
13
15
|
import { createJiti } from "jiti";
|
|
14
16
|
import YAML from "yaml";
|
|
15
17
|
import IconsResolver from "unplugin-icons/resolver";
|
|
@@ -32,18 +34,21 @@ import Markdown from "unplugin-vue-markdown/vite";
|
|
|
32
34
|
import MarkdownItComark from "@comark/markdown-it";
|
|
33
35
|
import MarkdownItFootnote from "markdown-it-footnote";
|
|
34
36
|
import MarkdownItGitHubAlerts from "markdown-it-github-alerts";
|
|
37
|
+
import { toKeyedTokens } from "@shikijs/magic-move/core";
|
|
35
38
|
import { defineCodeblockTransformer } from "@slidev/types";
|
|
36
39
|
import lz from "lz-string";
|
|
37
|
-
import { toKeyedTokens } from "shiki-magic-move/core";
|
|
38
40
|
import { encode } from "plantuml-encoder";
|
|
39
41
|
import { SourceMapConsumer } from "source-map-js";
|
|
40
42
|
import { fromAsyncCodeToHtml } from "@shikijs/markdown-it/async";
|
|
43
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
44
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
45
|
+
import { z } from "zod";
|
|
41
46
|
import ServerRef from "vite-plugin-vue-server-ref";
|
|
47
|
+
import { VitePWA } from "vite-plugin-pwa";
|
|
42
48
|
import UnoCSS from "unocss/vite";
|
|
43
49
|
import { mergeConfigs, presetIcons } from "unocss";
|
|
44
50
|
import Vue from "@vitejs/plugin-vue";
|
|
45
51
|
import VueJsx from "@vitejs/plugin-vue-jsx";
|
|
46
|
-
|
|
47
52
|
//#region node/syntax/link.ts
|
|
48
53
|
const RE_DIGITS_ONLY = /^\d+$/;
|
|
49
54
|
function MarkdownItLink(md) {
|
|
@@ -64,7 +69,6 @@ function MarkdownItLink(md) {
|
|
|
64
69
|
return defaultRender(tokens, idx, options, env, self);
|
|
65
70
|
};
|
|
66
71
|
}
|
|
67
|
-
|
|
68
72
|
//#endregion
|
|
69
73
|
//#region node/utils.ts
|
|
70
74
|
const RE_WHITESPACE_ONLY = /^\s*$/;
|
|
@@ -104,8 +108,8 @@ function updateFrontmatterPatch(source, frontmatter) {
|
|
|
104
108
|
else {
|
|
105
109
|
const valueNode = doc.createNode(value);
|
|
106
110
|
let found = false;
|
|
107
|
-
YAML.visit(doc.contents, { Pair(_key, node, path
|
|
108
|
-
if (path
|
|
111
|
+
YAML.visit(doc.contents, { Pair(_key, node, path) {
|
|
112
|
+
if (path.length === 1 && YAML.isScalar(node.key) && node.key.value === key) {
|
|
109
113
|
node.value = valueNode;
|
|
110
114
|
found = true;
|
|
111
115
|
return YAML.visit.BREAK;
|
|
@@ -119,13 +123,13 @@ function updateFrontmatterPatch(source, frontmatter) {
|
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
function getBodyJson(req) {
|
|
122
|
-
return new Promise((resolve
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
123
127
|
let body = "";
|
|
124
128
|
req.on("data", (chunk) => body += chunk);
|
|
125
129
|
req.on("error", reject);
|
|
126
130
|
req.on("end", () => {
|
|
127
131
|
try {
|
|
128
|
-
resolve
|
|
132
|
+
resolve(JSON.parse(body) || {});
|
|
129
133
|
} catch (e) {
|
|
130
134
|
reject(e);
|
|
131
135
|
}
|
|
@@ -137,39 +141,51 @@ function getImportGlobRelativePath(from, to) {
|
|
|
137
141
|
const normalizedTo = slash(to);
|
|
138
142
|
return slash(RE_WINDOWS_DRIVE.test(normalizedFrom) || RE_WINDOWS_DRIVE.test(normalizedTo) ? win32.relative(normalizedFrom, normalizedTo) : relative(normalizedFrom, normalizedTo));
|
|
139
143
|
}
|
|
140
|
-
function
|
|
141
|
-
|
|
144
|
+
function resolveImportGlobProxyModule(proxyBase, content) {
|
|
145
|
+
return `${proxyBase}.${createHash("sha256").update(content).digest("hex").slice(0, 10)}.ts`;
|
|
146
|
+
}
|
|
147
|
+
function createMakeAbsoluteImportGlob(baseRoot) {
|
|
148
|
+
const proxyModules = /* @__PURE__ */ new Map();
|
|
149
|
+
const proxyBase = slash(join(baseRoot, "node_modules/.slidev/virtual/import-glob"));
|
|
150
|
+
return function makeAbsoluteImportGlob(globs, options = {}) {
|
|
151
|
+
const content = `export default ${makeAbsoluteImportGlobExpression(dirname(proxyBase), globs, options)}\n`;
|
|
152
|
+
const proxyModule = resolveImportGlobProxyModule(proxyBase, content);
|
|
153
|
+
if (proxyModules.get(proxyModule) !== content) {
|
|
154
|
+
mkdirSync(dirname(proxyModule), { recursive: true });
|
|
155
|
+
writeFileSync(proxyModule, content, "utf-8");
|
|
156
|
+
proxyModules.set(proxyModule, content);
|
|
157
|
+
}
|
|
158
|
+
return toAtFS(proxyModule);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function makeAbsoluteImportGlobExpression(self, globs, options = {}) {
|
|
142
162
|
const relativeGlobs = globs.map((glob) => {
|
|
143
|
-
const relativeGlob = getImportGlobRelativePath(
|
|
144
|
-
return
|
|
163
|
+
const relativeGlob = getImportGlobRelativePath(self, glob);
|
|
164
|
+
return !relativeGlob.startsWith(".") && !RE_WINDOWS_DRIVE.test(relativeGlob) ? `./${relativeGlob}` : relativeGlob;
|
|
145
165
|
});
|
|
146
166
|
const opts = {
|
|
147
167
|
eager: true,
|
|
148
168
|
exhaustive: true,
|
|
149
|
-
...root ? { base: "/" } : {},
|
|
150
169
|
...options
|
|
151
170
|
};
|
|
152
171
|
return `import.meta.glob(${JSON.stringify(relativeGlobs)}, ${JSON.stringify(opts)})`;
|
|
153
172
|
}
|
|
154
|
-
|
|
155
173
|
//#endregion
|
|
156
174
|
//#region node/setups/load.ts
|
|
157
175
|
async function loadSetups(roots, filename, args, extraLoader) {
|
|
158
176
|
return await Promise.all(roots.flatMap((root) => {
|
|
159
177
|
const tasks = [];
|
|
160
|
-
const path
|
|
161
|
-
if (existsSync(path
|
|
178
|
+
const path = resolve(root, "setup", filename);
|
|
179
|
+
if (existsSync(path)) tasks.push(loadModule(path).then((mod) => mod.default(...args)));
|
|
162
180
|
if (extraLoader) tasks.push(...extraLoader(root));
|
|
163
181
|
return tasks;
|
|
164
182
|
}));
|
|
165
183
|
}
|
|
166
|
-
|
|
167
184
|
//#endregion
|
|
168
185
|
//#region node/setups/vite-plugins.ts
|
|
169
186
|
async function setupVitePlugins(options) {
|
|
170
187
|
return await loadSetups(options.roots, "vite-plugins.ts", [options]);
|
|
171
188
|
}
|
|
172
|
-
|
|
173
189
|
//#endregion
|
|
174
190
|
//#region node/vite/compilerFlagsVue.ts
|
|
175
191
|
const RE_VUE_FILE = /\.vue(?:$|\?)/;
|
|
@@ -181,8 +197,8 @@ function createVueCompilerFlagsPlugin(options) {
|
|
|
181
197
|
return {
|
|
182
198
|
name: "slidev:flags",
|
|
183
199
|
enforce: "pre",
|
|
184
|
-
transform: { handler(code, id
|
|
185
|
-
if (!RE_VUE_FILE.test(id
|
|
200
|
+
transform: { handler(code, id) {
|
|
201
|
+
if (!RE_VUE_FILE.test(id) && !id.includes("?vue&")) return;
|
|
186
202
|
const original = code;
|
|
187
203
|
define.forEach(([from, to]) => {
|
|
188
204
|
code = code.replaceAll(from, to);
|
|
@@ -191,7 +207,6 @@ function createVueCompilerFlagsPlugin(options) {
|
|
|
191
207
|
} }
|
|
192
208
|
};
|
|
193
209
|
}
|
|
194
|
-
|
|
195
210
|
//#endregion
|
|
196
211
|
//#region node/vite/components.ts
|
|
197
212
|
const RE_VUE_EXT$1 = /\.vue$/;
|
|
@@ -227,7 +242,6 @@ function createComponentsPlugin({ clientRoot, roots }, pluginOptions) {
|
|
|
227
242
|
...pluginOptions.components
|
|
228
243
|
});
|
|
229
244
|
}
|
|
230
|
-
|
|
231
245
|
//#endregion
|
|
232
246
|
//#region node/vite/common.ts
|
|
233
247
|
const regexSlideReqPath = /^\/__slidev\/slides\/(\d+)\.json$/;
|
|
@@ -236,7 +250,6 @@ const regexSlideSourceId = /__slidev_(\d+)\.(md|frontmatter)$/;
|
|
|
236
250
|
const templateInjectionMarker = "/* @slidev-injection */";
|
|
237
251
|
const templateImportContextUtils = `import { useSlideContext as _useSlideContext, frontmatterToProps as _frontmatterToProps } from "@slidev/client/context.ts"`;
|
|
238
252
|
const templateInitContext = `const { $slidev, $nav, $clicksContext, $clicks, $page, $renderContext, $frontmatter } = _useSlideContext()`;
|
|
239
|
-
|
|
240
253
|
//#endregion
|
|
241
254
|
//#region node/vite/contextInjection.ts
|
|
242
255
|
const RE_EXPORT_DEFAULT_OBJECT = /export\s+default\s+\{/;
|
|
@@ -247,9 +260,9 @@ const RE_INJECT_OPTION = /.*inject\s*:\s*([[{])/;
|
|
|
247
260
|
function createContextInjectionPlugin() {
|
|
248
261
|
return {
|
|
249
262
|
name: "slidev:context-injection",
|
|
250
|
-
transform: { async handler(code, id
|
|
251
|
-
if (!id
|
|
252
|
-
if (code.includes(
|
|
263
|
+
transform: { async handler(code, id) {
|
|
264
|
+
if (!id.endsWith(".vue") || id.includes("/@slidev/client/") || id.includes("/packages/client/")) return;
|
|
265
|
+
if (code.includes("/* @slidev-injection */") || code.includes("useSlideContext()")) return code;
|
|
253
266
|
const imports = [
|
|
254
267
|
templateImportContextUtils,
|
|
255
268
|
templateInitContext,
|
|
@@ -291,7 +304,6 @@ ${code.slice(scriptIndex)}`;
|
|
|
291
304
|
} }
|
|
292
305
|
};
|
|
293
306
|
}
|
|
294
|
-
|
|
295
307
|
//#endregion
|
|
296
308
|
//#region node/vite/extendConfig.ts
|
|
297
309
|
const RE_SLIDEV_CLIENT = /^@slidev\/client$/;
|
|
@@ -299,6 +311,7 @@ const RE_SLIDEV_CLIENT_SUBPATH = /^@slidev\/client\/(.*)/;
|
|
|
299
311
|
const RE_SLIDEV_VIRTUAL = /^#slidev\/(.*)/;
|
|
300
312
|
const RE_MONACO_EDITOR = /\/monaco-editor(?:-core)?\//;
|
|
301
313
|
const INCLUDE_GLOBAL = [
|
|
314
|
+
"@fix-webm-duration/fix",
|
|
302
315
|
"@typescript/ata",
|
|
303
316
|
"file-saver",
|
|
304
317
|
"lz-string",
|
|
@@ -311,6 +324,7 @@ const INCLUDE_GLOBAL = [
|
|
|
311
324
|
const INCLUDE_LOCAL = INCLUDE_GLOBAL.map((i) => `@slidev/cli > @slidev/client > ${i}`);
|
|
312
325
|
const EXCLUDE_GLOBAL = [
|
|
313
326
|
"@antfu/utils",
|
|
327
|
+
"@shikijs/magic-move/vue",
|
|
314
328
|
"@shikijs/monaco",
|
|
315
329
|
"@shikijs/vitepress-twoslash/client",
|
|
316
330
|
"@slidev/client",
|
|
@@ -332,7 +346,6 @@ const EXCLUDE_GLOBAL = [
|
|
|
332
346
|
"fuse.js",
|
|
333
347
|
"mermaid",
|
|
334
348
|
"monaco-editor",
|
|
335
|
-
"shiki-magic-move/vue",
|
|
336
349
|
"shiki",
|
|
337
350
|
"shiki/core",
|
|
338
351
|
"vue-demi",
|
|
@@ -412,18 +425,18 @@ function createConfigPlugin(options) {
|
|
|
412
425
|
if (chunkInfo.moduleIds.filter((i) => i.match(RE_MONACO_EDITOR)).length > chunkInfo.moduleIds.length * .6) return "assets/monaco/[name]-[hash].js";
|
|
413
426
|
return DEFAULT;
|
|
414
427
|
},
|
|
415
|
-
manualChunks(id
|
|
416
|
-
if (id
|
|
417
|
-
if (id
|
|
418
|
-
if (id
|
|
419
|
-
const matchedAsyncModule = ASYNC_MODULES.find((i) => id
|
|
428
|
+
manualChunks(id) {
|
|
429
|
+
if (id.startsWith("/@slidev-monaco-types/") || id.includes("/@slidev/monaco-types") || id.endsWith("?monaco-types&raw")) return "monaco/bundled-types";
|
|
430
|
+
if (id.includes("/shiki/") || id.includes("/@shikijs/")) return `modules/shiki`;
|
|
431
|
+
if (id.startsWith("~icons/")) return "modules/unplugin-icons";
|
|
432
|
+
const matchedAsyncModule = ASYNC_MODULES.find((i) => id.includes(`/node_modules/${i}`));
|
|
420
433
|
if (matchedAsyncModule) return `modules/${matchedAsyncModule.replace("@", "").replace("/", "-")}`;
|
|
421
434
|
}
|
|
422
435
|
} } },
|
|
423
436
|
cacheDir: isInstalledGlobally.value ? join(options.cliRoot, "node_modules/.vite") : void 0
|
|
424
437
|
};
|
|
425
|
-
function isSlidevClient(id
|
|
426
|
-
return id
|
|
438
|
+
function isSlidevClient(id) {
|
|
439
|
+
return id.includes("/@slidev/") || id.includes("/slidev/packages/client/") || id.includes("/@vueuse/");
|
|
427
440
|
}
|
|
428
441
|
return mergeConfig(injection, config);
|
|
429
442
|
},
|
|
@@ -444,7 +457,6 @@ function createConfigPlugin(options) {
|
|
|
444
457
|
}
|
|
445
458
|
};
|
|
446
459
|
}
|
|
447
|
-
|
|
448
460
|
//#endregion
|
|
449
461
|
//#region node/vite/hmrPatch.ts
|
|
450
462
|
/**
|
|
@@ -455,14 +467,13 @@ function createHmrPatchPlugin() {
|
|
|
455
467
|
name: "slidev:hmr-patch",
|
|
456
468
|
transform: {
|
|
457
469
|
filter: { id: { include: regexSlideSourceId } },
|
|
458
|
-
handler(code, id
|
|
459
|
-
if (!id
|
|
470
|
+
handler(code, id) {
|
|
471
|
+
if (!id.match(regexSlideSourceId)) return;
|
|
460
472
|
return code.replace("if (_rerender_only)", "if (false)");
|
|
461
473
|
}
|
|
462
474
|
}
|
|
463
475
|
};
|
|
464
476
|
}
|
|
465
|
-
|
|
466
477
|
//#endregion
|
|
467
478
|
//#region node/vite/icons.ts
|
|
468
479
|
function createIconsPlugin(options, pluginOptions) {
|
|
@@ -472,7 +483,6 @@ function createIconsPlugin(options, pluginOptions) {
|
|
|
472
483
|
...pluginOptions.icons
|
|
473
484
|
});
|
|
474
485
|
}
|
|
475
|
-
|
|
476
486
|
//#endregion
|
|
477
487
|
//#region node/virtual/deprecated.ts
|
|
478
488
|
/**
|
|
@@ -505,7 +515,6 @@ console.warn('/@slidev/titles.md is deprecated, import from #slidev/title-render
|
|
|
505
515
|
`;
|
|
506
516
|
}
|
|
507
517
|
};
|
|
508
|
-
|
|
509
518
|
//#endregion
|
|
510
519
|
//#region node/virtual/titles.ts
|
|
511
520
|
const templateTitleRendererMd = {
|
|
@@ -522,10 +531,9 @@ const templateTitleRenderer = {
|
|
|
522
531
|
return "export { default } from \"/@slidev/title-renderer.md\"";
|
|
523
532
|
}
|
|
524
533
|
};
|
|
525
|
-
|
|
526
534
|
//#endregion
|
|
527
535
|
//#region node/vite/importGuard.ts
|
|
528
|
-
const virtualSlideMarkdownIds = new Set([templateTitleRendererMd.id, templateLegacyTitles.id]);
|
|
536
|
+
const virtualSlideMarkdownIds = /* @__PURE__ */ new Set([templateTitleRendererMd.id, templateLegacyTitles.id]);
|
|
529
537
|
function createSlideImportGuardPlugin() {
|
|
530
538
|
let config;
|
|
531
539
|
let allowRoots = [];
|
|
@@ -535,10 +543,10 @@ function createSlideImportGuardPlugin() {
|
|
|
535
543
|
config = resolved;
|
|
536
544
|
allowRoots = config.server.fs.allow.map(normalizeFsPath);
|
|
537
545
|
},
|
|
538
|
-
async transform(code, id
|
|
539
|
-
if (!isSlideMarkdownId(id
|
|
540
|
-
const importer = filePathFromId(id
|
|
541
|
-
await Promise.all(extractImportSources(code, id
|
|
546
|
+
async transform(code, id) {
|
|
547
|
+
if (!isSlideMarkdownId(id) || !config?.server.fs.strict) return null;
|
|
548
|
+
const importer = filePathFromId(id) ?? id;
|
|
549
|
+
await Promise.all(extractImportSources(code, id).map(async ({ value, start }) => {
|
|
542
550
|
const resolved = await this.resolve(value, importer, { skipSelf: true });
|
|
543
551
|
if (!resolved || resolved.external) return;
|
|
544
552
|
const filePath = filePathFromId(resolved.id);
|
|
@@ -552,12 +560,12 @@ function createSlideImportGuardPlugin() {
|
|
|
552
560
|
}
|
|
553
561
|
};
|
|
554
562
|
}
|
|
555
|
-
function isSlideMarkdownId(id
|
|
556
|
-
const clean = cleanUrl$1(id
|
|
563
|
+
function isSlideMarkdownId(id) {
|
|
564
|
+
const clean = cleanUrl$1(id);
|
|
557
565
|
return regexSlideSourceId.test(clean) || virtualSlideMarkdownIds.has(clean);
|
|
558
566
|
}
|
|
559
|
-
function filePathFromId(id
|
|
560
|
-
const clean = cleanUrl$1(id
|
|
567
|
+
function filePathFromId(id) {
|
|
568
|
+
const clean = cleanUrl$1(id);
|
|
561
569
|
if (clean.startsWith("file://")) return fileURLToPath(clean);
|
|
562
570
|
if (clean.startsWith("/@fs/")) return clean.slice(4);
|
|
563
571
|
if (clean.startsWith("/@")) return null;
|
|
@@ -567,8 +575,8 @@ function filePathFromId(id$2) {
|
|
|
567
575
|
function isAllowedFile(filePath, allowRoots) {
|
|
568
576
|
return allowRoots.some((root) => isFileInRoot(root, filePath));
|
|
569
577
|
}
|
|
570
|
-
function extractImportSources(code, id
|
|
571
|
-
const result = parseSync(id
|
|
578
|
+
function extractImportSources(code, id) {
|
|
579
|
+
const result = parseSync(id, code);
|
|
572
580
|
const sources = [];
|
|
573
581
|
for (const item of result.module.staticImports) sources.push({
|
|
574
582
|
value: item.moduleRequest.value,
|
|
@@ -587,8 +595,8 @@ function extractImportSources(code, id$2) {
|
|
|
587
595
|
}
|
|
588
596
|
return sources;
|
|
589
597
|
}
|
|
590
|
-
function cleanUrl$1(id
|
|
591
|
-
return id
|
|
598
|
+
function cleanUrl$1(id) {
|
|
599
|
+
return id.replace(/[?#].*$/, "");
|
|
592
600
|
}
|
|
593
601
|
function normalizeFsPath(filePath) {
|
|
594
602
|
const absolute = path.resolve(filePath);
|
|
@@ -602,8 +610,8 @@ function normalizeMissingFsPath(filePath) {
|
|
|
602
610
|
return path.join(normalizeMissingFsPath(dir), path.basename(filePath));
|
|
603
611
|
}
|
|
604
612
|
function isFileInRoot(root, filePath) {
|
|
605
|
-
const relative
|
|
606
|
-
return relative
|
|
613
|
+
const relative = path.relative(root, filePath);
|
|
614
|
+
return relative === "" || !!relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
607
615
|
}
|
|
608
616
|
function isDependencyFile(filePath) {
|
|
609
617
|
return filePath.split(/[\\/]/).includes("node_modules");
|
|
@@ -624,7 +632,6 @@ function parseStringLiteral(raw) {
|
|
|
624
632
|
return trimmed.slice(1, -1);
|
|
625
633
|
}
|
|
626
634
|
}
|
|
627
|
-
|
|
628
635
|
//#endregion
|
|
629
636
|
//#region node/vite/inspect.ts
|
|
630
637
|
async function createInspectPlugin(options, pluginOptions) {
|
|
@@ -636,7 +643,6 @@ async function createInspectPlugin(options, pluginOptions) {
|
|
|
636
643
|
...pluginOptions.inspect
|
|
637
644
|
});
|
|
638
645
|
}
|
|
639
|
-
|
|
640
646
|
//#endregion
|
|
641
647
|
//#region node/vite/layoutWrapper.ts
|
|
642
648
|
const RE_SCRIPT_SETUP_TAG = /^<script setup.*>/m;
|
|
@@ -645,8 +651,8 @@ function createLayoutWrapperPlugin({ data, utils }) {
|
|
|
645
651
|
name: "slidev:layout-wrapper",
|
|
646
652
|
transform: {
|
|
647
653
|
filter: { id: { include: regexSlideSourceId } },
|
|
648
|
-
async handler(code, id
|
|
649
|
-
const match = id
|
|
654
|
+
async handler(code, id) {
|
|
655
|
+
const match = id.match(regexSlideSourceId);
|
|
650
656
|
if (!match) return;
|
|
651
657
|
const [, no, type] = match;
|
|
652
658
|
if (type !== "md") return;
|
|
@@ -682,11 +688,9 @@ function createLayoutWrapperPlugin({ data, utils }) {
|
|
|
682
688
|
}
|
|
683
689
|
};
|
|
684
690
|
}
|
|
685
|
-
|
|
686
691
|
//#endregion
|
|
687
692
|
//#region package.json
|
|
688
|
-
var version = "52.
|
|
689
|
-
|
|
693
|
+
var version = "52.17.0";
|
|
690
694
|
//#endregion
|
|
691
695
|
//#region node/integrations/addons.ts
|
|
692
696
|
async function resolveAddons(addonsInConfig) {
|
|
@@ -705,26 +709,21 @@ async function resolveAddons(addonsInConfig) {
|
|
|
705
709
|
if (Array.isArray(userPkgJson.slidev?.addons)) await Promise.all(userPkgJson.slidev.addons.map((addon) => resolveAddon(addon, userRoot)));
|
|
706
710
|
return resolved;
|
|
707
711
|
}
|
|
708
|
-
|
|
709
|
-
//#endregion
|
|
710
|
-
//#region node/integrations/themes.ts
|
|
711
|
-
const officialThemes = {
|
|
712
|
+
const resolveTheme = createResolver("theme", {
|
|
712
713
|
"none": "",
|
|
713
714
|
"default": "@slidev/theme-default",
|
|
714
715
|
"seriph": "@slidev/theme-seriph",
|
|
715
716
|
"apple-basic": "@slidev/theme-apple-basic",
|
|
716
717
|
"shibainu": "@slidev/theme-shibainu",
|
|
717
718
|
"bricks": "@slidev/theme-bricks"
|
|
718
|
-
};
|
|
719
|
-
const resolveTheme = createResolver("theme", officialThemes);
|
|
719
|
+
});
|
|
720
720
|
async function getThemeMeta(name, root) {
|
|
721
|
-
const path
|
|
722
|
-
if (!existsSync(path
|
|
723
|
-
const { slidev = {}, engines = {} } = JSON.parse(await fs$1.readFile(path
|
|
721
|
+
const path = join(root, "package.json");
|
|
722
|
+
if (!existsSync(path)) return {};
|
|
723
|
+
const { slidev = {}, engines = {} } = JSON.parse(await fs$1.readFile(path, "utf-8"));
|
|
724
724
|
if (engines.slidev && !satisfies(version, engines.slidev, { includePrerelease: true })) throw new Error(`[slidev] theme "${name}" requires Slidev version range "${engines.slidev}" but found "${version}"`);
|
|
725
725
|
return slidev;
|
|
726
726
|
}
|
|
727
|
-
|
|
728
727
|
//#endregion
|
|
729
728
|
//#region node/setups/indexHtml.ts
|
|
730
729
|
const RE_TRAILING_SLASH = /\/$/;
|
|
@@ -761,11 +760,11 @@ async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data,
|
|
|
761
760
|
let body = "";
|
|
762
761
|
const inputs = [];
|
|
763
762
|
for (const root of roots) {
|
|
764
|
-
const path
|
|
765
|
-
if (!existsSync(path
|
|
766
|
-
const html = await readFile(path
|
|
763
|
+
const path = join(root, "index.html");
|
|
764
|
+
if (!existsSync(path)) continue;
|
|
765
|
+
const html = await readFile(path, "utf-8");
|
|
767
766
|
if (root === userRoot && html.includes("<!DOCTYPE")) {
|
|
768
|
-
console.error(yellow(`[Slidev] Ignored provided index.html with doctype declaration. (${white(path
|
|
767
|
+
console.error(yellow(`[Slidev] Ignored provided index.html with doctype declaration. (${white(path)})`));
|
|
769
768
|
console.error(yellow("This file may be generated by Slidev, please remove it from your project."));
|
|
770
769
|
continue;
|
|
771
770
|
}
|
|
@@ -815,7 +814,7 @@ async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data,
|
|
|
815
814
|
},
|
|
816
815
|
{
|
|
817
816
|
property: "slidev:entry",
|
|
818
|
-
content: mode === "dev"
|
|
817
|
+
content: mode === "dev" ? slash(entry) : null
|
|
819
818
|
},
|
|
820
819
|
{
|
|
821
820
|
name: "description",
|
|
@@ -878,16 +877,14 @@ async function setupIndexHtml({ mode, entry, clientRoot, userRoot, roots, data,
|
|
|
878
877
|
main = main.replace("__ENTRY__", encodeURI(basePrefix + mainUrl));
|
|
879
878
|
}
|
|
880
879
|
main = main.replace("<!-- body -->", body);
|
|
881
|
-
return
|
|
880
|
+
return transformHtmlTemplate(unhead, main);
|
|
882
881
|
}
|
|
883
|
-
|
|
884
882
|
//#endregion
|
|
885
883
|
//#region node/setups/katex.ts
|
|
886
884
|
async function setupKatex(roots) {
|
|
887
885
|
const options = await loadSetups(roots, "katex.ts", []);
|
|
888
886
|
return Object.assign({ strict: false }, ...options);
|
|
889
887
|
}
|
|
890
|
-
|
|
891
888
|
//#endregion
|
|
892
889
|
//#region ../client/setup/shiki-options.ts
|
|
893
890
|
function resolveShikiOptions(options) {
|
|
@@ -909,7 +906,7 @@ function resolveShikiOptions(options) {
|
|
|
909
906
|
if (mergedOptions.themes) {
|
|
910
907
|
for (const theme of Object.values(mergedOptions.themes)) if (typeof theme === "object" && theme?.name) themeInput[theme.name] = theme;
|
|
911
908
|
}
|
|
912
|
-
const languageNames = new Set([
|
|
909
|
+
const languageNames = /* @__PURE__ */ new Set([
|
|
913
910
|
"markdown",
|
|
914
911
|
"vue",
|
|
915
912
|
"javascript",
|
|
@@ -959,16 +956,17 @@ function extractThemeNames(themes) {
|
|
|
959
956
|
return [key, name];
|
|
960
957
|
});
|
|
961
958
|
}
|
|
962
|
-
|
|
963
959
|
//#endregion
|
|
964
960
|
//#region node/setups/shiki.ts
|
|
965
961
|
let cachedRoots;
|
|
966
962
|
let cachedShiki;
|
|
967
963
|
async function setupShiki(roots) {
|
|
968
964
|
if (cachedRoots === roots) return cachedShiki;
|
|
969
|
-
const { options, languageInput, themeInput } = resolveShikiOptions(await loadSetups(roots, "shiki.ts", [{
|
|
965
|
+
const { options, languageInput, themeInput } = resolveShikiOptions(await loadSetups(roots, "shiki.ts", [{
|
|
966
|
+
/** @deprecated */
|
|
967
|
+
async loadTheme(path) {
|
|
970
968
|
console.warn("[slidev] `loadTheme` in `setup/shiki.ts` is deprecated. Pass directly the theme name it's supported by Shiki. For custom themes, load it manually via `JSON.parse(fs.readFileSync(path, 'utf-8'))` and pass the raw JSON object instead.");
|
|
971
|
-
return JSON.parse(await fs$1.readFile(path
|
|
969
|
+
return JSON.parse(await fs$1.readFile(path, "utf-8"));
|
|
972
970
|
} }]));
|
|
973
971
|
const createHighlighter = createBundledHighlighter({
|
|
974
972
|
engine: createJavaScriptRegexEngine,
|
|
@@ -981,7 +979,6 @@ async function setupShiki(roots) {
|
|
|
981
979
|
shikiOptions: options
|
|
982
980
|
};
|
|
983
981
|
}
|
|
984
|
-
|
|
985
982
|
//#endregion
|
|
986
983
|
//#region node/options.ts
|
|
987
984
|
const RE_FILE_EXTENSION = /\.\w+$/;
|
|
@@ -989,7 +986,10 @@ const debug = createDebug("slidev:options");
|
|
|
989
986
|
async function resolveOptions(entryOptions, mode) {
|
|
990
987
|
const entry = await resolveEntry(entryOptions.entry);
|
|
991
988
|
const rootsInfo = await getRoots(entry);
|
|
992
|
-
const loaded = await parser.load(
|
|
989
|
+
const loaded = await parser.load({
|
|
990
|
+
userRoot: rootsInfo.userRoot,
|
|
991
|
+
roots: [rootsInfo.userRoot]
|
|
992
|
+
}, entry, void 0, mode);
|
|
993
993
|
let themeRaw = entryOptions.theme || loaded.headmatter.theme;
|
|
994
994
|
themeRaw = themeRaw === null ? "none" : themeRaw || "default";
|
|
995
995
|
const [theme, themeRoot] = await resolveTheme(themeRaw, entry);
|
|
@@ -1076,6 +1076,7 @@ function getDefine(options) {
|
|
|
1076
1076
|
__DEV__: options.mode === "dev",
|
|
1077
1077
|
__SLIDEV_CLIENT_ROOT__: toAtFS(options.clientRoot),
|
|
1078
1078
|
__SLIDEV_HASH_ROUTE__: options.data.config.routerMode === "hash",
|
|
1079
|
+
__SLIDEV_MEMORY_ROUTE__: options.data.config.routerMode === "memory" && options.mode !== "export",
|
|
1079
1080
|
__SLIDEV_FEATURE_DRAWINGS__: matchMode(options.data.config.drawings.enabled),
|
|
1080
1081
|
__SLIDEV_FEATURE_EDITOR__: options.mode === "dev" && options.data.config.editor !== false,
|
|
1081
1082
|
__SLIDEV_FEATURE_DRAWINGS_PERSIST__: !!options.data.config.drawings.persist,
|
|
@@ -1088,10 +1089,10 @@ function getDefine(options) {
|
|
|
1088
1089
|
].includes(options.data.config.download),
|
|
1089
1090
|
__SLIDEV_FEATURE_BROWSER_EXPORTER__: matchMode(options.data.config.browserExporter),
|
|
1090
1091
|
__SLIDEV_FEATURE_WAKE_LOCK__: matchMode(options.data.config.wakeLock),
|
|
1092
|
+
__SLIDEV_FEATURE_PWA__: matchMode(options.data.config.pwa),
|
|
1091
1093
|
__SLIDEV_HAS_SERVER__: options.mode !== "build"
|
|
1092
1094
|
}, (v, k) => [v, JSON.stringify(k)]);
|
|
1093
1095
|
}
|
|
1094
|
-
|
|
1095
1096
|
//#endregion
|
|
1096
1097
|
//#region node/syntax/utils.ts
|
|
1097
1098
|
function normalizeRangeStr(rangeStr = "") {
|
|
@@ -1103,7 +1104,6 @@ function normalizeRangeStr(rangeStr = "") {
|
|
|
1103
1104
|
function escapeVueInCode(md) {
|
|
1104
1105
|
return md.replace(/\{\{/g, "{{");
|
|
1105
1106
|
}
|
|
1106
|
-
|
|
1107
1107
|
//#endregion
|
|
1108
1108
|
//#region node/syntax/katex.ts
|
|
1109
1109
|
const RE_KATEX_BLOCK_INFO = /^\{([\w*,|-]+)\}\s*(\{[^}]*\})?/;
|
|
@@ -1228,9 +1228,9 @@ function MarkdownItKatex(md, options) {
|
|
|
1228
1228
|
};
|
|
1229
1229
|
const blockRenderer = function(tokens, idx) {
|
|
1230
1230
|
const token = tokens[idx];
|
|
1231
|
-
const [, rangeStr, options
|
|
1231
|
+
const [, rangeStr, options] = RE_KATEX_BLOCK_INFO.exec(token.info) || [];
|
|
1232
1232
|
const ranges = normalizeRangeStr(rangeStr);
|
|
1233
|
-
return `<KaTexBlockWrapper ${options
|
|
1233
|
+
return `<KaTexBlockWrapper ${options ? `v-bind="${options}"` : ""} :ranges='${JSON.stringify(ranges)}'>${katexBlock(tokens[idx].content)}</KaTexBlockWrapper>\n`;
|
|
1234
1234
|
};
|
|
1235
1235
|
md.inline.ruler.after("escape", "math_inline", math_inline);
|
|
1236
1236
|
md.block.ruler.after("blockquote", "math_block", math_block, { alt: [
|
|
@@ -1242,24 +1242,22 @@ function MarkdownItKatex(md, options) {
|
|
|
1242
1242
|
md.renderer.rules.math_inline = inlineRenderer;
|
|
1243
1243
|
md.renderer.rules.math_block = blockRenderer;
|
|
1244
1244
|
}
|
|
1245
|
-
|
|
1246
|
-
//#endregion
|
|
1247
|
-
//#region node/virtual/conditional-styles.ts
|
|
1248
|
-
const id$1 = "/@slidev/conditional-styles";
|
|
1249
1245
|
const templateConditionalStyles = {
|
|
1250
|
-
id:
|
|
1251
|
-
async getContent({ data, roots
|
|
1246
|
+
id: "/@slidev/conditional-styles",
|
|
1247
|
+
async getContent({ data, roots }) {
|
|
1252
1248
|
const imports = [];
|
|
1253
|
-
for (const root of roots)
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1249
|
+
for (const root of roots) {
|
|
1250
|
+
const importPath = this.makeAbsoluteImportGlob([
|
|
1251
|
+
join(root, "styles/index.{ts,js,css}"),
|
|
1252
|
+
join(root, "styles.{ts,js,css}"),
|
|
1253
|
+
join(root, "style.{ts,js,css}")
|
|
1254
|
+
]);
|
|
1255
|
+
imports.push(`import ${JSON.stringify(importPath)}`);
|
|
1256
|
+
}
|
|
1258
1257
|
if (data.features.katex) imports.push(`import "${await resolveImportUrl("katex/dist/katex.min.css")}"`);
|
|
1259
1258
|
return imports.join("\n");
|
|
1260
1259
|
}
|
|
1261
1260
|
};
|
|
1262
|
-
|
|
1263
1261
|
//#endregion
|
|
1264
1262
|
//#region node/virtual/configs.ts
|
|
1265
1263
|
const templateConfigs = {
|
|
@@ -1274,26 +1272,24 @@ const templateConfigs = {
|
|
|
1274
1272
|
return `export default ${JSON.stringify(config)}`;
|
|
1275
1273
|
}
|
|
1276
1274
|
};
|
|
1277
|
-
|
|
1278
|
-
//#endregion
|
|
1279
|
-
//#region node/virtual/global-layers.ts
|
|
1280
|
-
const id = `/@slidev/global-layers`;
|
|
1281
1275
|
const templateGlobalLayers = {
|
|
1282
|
-
id
|
|
1283
|
-
getContent({ roots
|
|
1276
|
+
id: `/@slidev/global-layers`,
|
|
1277
|
+
getContent({ roots }) {
|
|
1278
|
+
const { makeAbsoluteImportGlob } = this;
|
|
1279
|
+
const imports = [`import { h } from 'vue'`];
|
|
1280
|
+
let importIndex = 0;
|
|
1284
1281
|
function* getComponent(name, names) {
|
|
1285
1282
|
yield `const ${name}Components = [\n`;
|
|
1286
1283
|
for (const root of roots) {
|
|
1287
|
-
const globs = names.map((name
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
yield
|
|
1284
|
+
const globs = names.map((name) => join(root, `${name}.{ts,js,vue}`));
|
|
1285
|
+
const importName = `__slidev_global_layer_${importIndex++}`;
|
|
1286
|
+
imports.push(`import ${importName} from ${JSON.stringify(makeAbsoluteImportGlob(globs, { import: "default" }))}`);
|
|
1287
|
+
yield ` Object.values(${importName})[0],\n`;
|
|
1291
1288
|
}
|
|
1292
1289
|
yield `].filter(Boolean)\n`;
|
|
1293
1290
|
yield `export const ${name} = { render: () => ${name}Components.map(comp => h(comp)) }\n\n`;
|
|
1294
1291
|
}
|
|
1295
|
-
|
|
1296
|
-
`import { h } from 'vue'\n\n`,
|
|
1292
|
+
const body = [
|
|
1297
1293
|
...getComponent("GlobalTop", [
|
|
1298
1294
|
"global",
|
|
1299
1295
|
"global-top",
|
|
@@ -1303,9 +1299,9 @@ const templateGlobalLayers = {
|
|
|
1303
1299
|
...getComponent("SlideTop", ["slide-top", "SlideTop"]),
|
|
1304
1300
|
...getComponent("SlideBottom", ["slide-bottom", "SlideBottom"])
|
|
1305
1301
|
].join("");
|
|
1302
|
+
return `${imports.join("\n")}\n\n${body}`;
|
|
1306
1303
|
}
|
|
1307
1304
|
};
|
|
1308
|
-
|
|
1309
1305
|
//#endregion
|
|
1310
1306
|
//#region node/virtual/layouts.ts
|
|
1311
1307
|
const templateLayouts = {
|
|
@@ -1319,7 +1315,6 @@ const templateLayouts = {
|
|
|
1319
1315
|
return [imports.join("\n"), `export default {\n${Object.entries(layouts).map(([k, v]) => `"${k}": ${v}`).join(",\n")}\n}`].join("\n\n");
|
|
1320
1316
|
}
|
|
1321
1317
|
};
|
|
1322
|
-
|
|
1323
1318
|
//#endregion
|
|
1324
1319
|
//#region node/virtual/monaco-deps.ts
|
|
1325
1320
|
const templateMonacoRunDeps = {
|
|
@@ -1345,7 +1340,6 @@ const templateMonacoRunDeps = {
|
|
|
1345
1340
|
return result;
|
|
1346
1341
|
}
|
|
1347
1342
|
};
|
|
1348
|
-
|
|
1349
1343
|
//#endregion
|
|
1350
1344
|
//#region node/virtual/monaco-types.ts
|
|
1351
1345
|
const templateMonacoTypes = {
|
|
@@ -1382,7 +1376,6 @@ const templateMonacoTypes = {
|
|
|
1382
1376
|
return result;
|
|
1383
1377
|
}
|
|
1384
1378
|
};
|
|
1385
|
-
|
|
1386
1379
|
//#endregion
|
|
1387
1380
|
//#region node/virtual/nav-controls.ts
|
|
1388
1381
|
const templateNavControls = {
|
|
@@ -1398,21 +1391,24 @@ export default {
|
|
|
1398
1391
|
}`;
|
|
1399
1392
|
}
|
|
1400
1393
|
};
|
|
1401
|
-
|
|
1402
1394
|
//#endregion
|
|
1403
1395
|
//#region node/virtual/setups.ts
|
|
1404
1396
|
function createSetupTemplate(name) {
|
|
1405
|
-
const id$2 = `/@slidev/setups/${name}`;
|
|
1406
1397
|
return {
|
|
1407
|
-
id:
|
|
1408
|
-
getContent({ roots
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1398
|
+
id: `/@slidev/setups/${name}`,
|
|
1399
|
+
getContent({ roots }) {
|
|
1400
|
+
const imports = [];
|
|
1401
|
+
const globs = roots.map((root) => {
|
|
1402
|
+
const glob = join(root, `setup/${name}.{ts,js,mts,mjs}`);
|
|
1403
|
+
const importName = `__slidev_setup_${imports.length}`;
|
|
1404
|
+
imports.push(`import ${importName} from ${JSON.stringify(this.makeAbsoluteImportGlob([glob], { import: "default" }))}`);
|
|
1405
|
+
return `Object.values(${importName})[0]`;
|
|
1406
|
+
});
|
|
1407
|
+
return `${imports.join("\n")}\n\nexport default [${globs.join(", ")}].filter(Boolean)`;
|
|
1412
1408
|
}
|
|
1413
1409
|
};
|
|
1414
1410
|
}
|
|
1415
|
-
const
|
|
1411
|
+
const templateSetups = [
|
|
1416
1412
|
"shiki",
|
|
1417
1413
|
"code-runners",
|
|
1418
1414
|
"monaco",
|
|
@@ -1423,9 +1419,7 @@ const setupModules = [
|
|
|
1423
1419
|
"routes",
|
|
1424
1420
|
"shortcuts",
|
|
1425
1421
|
"context-menu"
|
|
1426
|
-
];
|
|
1427
|
-
const templateSetups = setupModules.map(createSetupTemplate);
|
|
1428
|
-
|
|
1422
|
+
].map(createSetupTemplate);
|
|
1429
1423
|
//#endregion
|
|
1430
1424
|
//#region node/virtual/slides.ts
|
|
1431
1425
|
const VIRTUAL_SLIDE_PREFIX = "/@slidev/slides/";
|
|
@@ -1463,7 +1457,6 @@ const templateSlides = {
|
|
|
1463
1457
|
].join("\n");
|
|
1464
1458
|
}
|
|
1465
1459
|
};
|
|
1466
|
-
|
|
1467
1460
|
//#endregion
|
|
1468
1461
|
//#region node/virtual/index.ts
|
|
1469
1462
|
const templates = [
|
|
@@ -1481,7 +1474,6 @@ const templates = [
|
|
|
1481
1474
|
templateLegacyRoutes,
|
|
1482
1475
|
templateLegacyTitles
|
|
1483
1476
|
];
|
|
1484
|
-
|
|
1485
1477
|
//#endregion
|
|
1486
1478
|
//#region node/vite/loaders.ts
|
|
1487
1479
|
const RE_WORD_CHARS_ONLY = /^[\w-]+$/;
|
|
@@ -1493,13 +1485,14 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1493
1485
|
const hmrSlidesIndexes = /* @__PURE__ */ new Set();
|
|
1494
1486
|
let server;
|
|
1495
1487
|
let skipHmr = null;
|
|
1488
|
+
const makeAbsoluteImportGlob = createMakeAbsoluteImportGlob(options.userRoot);
|
|
1496
1489
|
let sourceIds = resolveSourceIds(data);
|
|
1497
|
-
function resolveSourceIds(data
|
|
1490
|
+
function resolveSourceIds(data) {
|
|
1498
1491
|
const ids = {
|
|
1499
1492
|
md: [],
|
|
1500
1493
|
frontmatter: []
|
|
1501
1494
|
};
|
|
1502
|
-
for (const type of ["md", "frontmatter"]) for (let i = 0; i < data
|
|
1495
|
+
for (const type of ["md", "frontmatter"]) for (let i = 0; i < data.slides.length; i++) ids[type].push(`${data.slides[i].source.filepath}__slidev_${i + 1}.${type}`);
|
|
1503
1496
|
return ids;
|
|
1504
1497
|
}
|
|
1505
1498
|
function updateServerWatcher() {
|
|
@@ -1537,7 +1530,7 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1537
1530
|
if (parsed.errors.length) console.error("ERROR when saving frontmatter", parsed.errors);
|
|
1538
1531
|
else slide.source.frontmatterDoc = parsed;
|
|
1539
1532
|
}
|
|
1540
|
-
if (body.note) slide.note = slide.source.note = body.note;
|
|
1533
|
+
if (body.note != null) slide.note = slide.source.note = body.note;
|
|
1541
1534
|
if (body.frontmatter) {
|
|
1542
1535
|
updateFrontmatterPatch(slide.source, body.frontmatter);
|
|
1543
1536
|
Object.assign(slide.frontmatter, body.frontmatter);
|
|
@@ -1625,32 +1618,38 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1625
1618
|
const moduleEntries = [
|
|
1626
1619
|
...ctx.modules.filter((i) => i.id === templateMonacoRunDeps.id || i.id === templateMonacoTypes.id),
|
|
1627
1620
|
...vueModules,
|
|
1628
|
-
...Array.from(moduleIds).map((id
|
|
1621
|
+
...Array.from(moduleIds).map((id) => ctx.server.moduleGraph.getModuleById(id))
|
|
1629
1622
|
].filter(notNullish).filter((i) => !i.id?.startsWith("/@id/@vite-icons"));
|
|
1630
1623
|
updateServerWatcher();
|
|
1631
1624
|
return moduleEntries;
|
|
1632
1625
|
},
|
|
1633
1626
|
resolveId: {
|
|
1634
1627
|
order: "pre",
|
|
1635
|
-
handler(id
|
|
1636
|
-
if (id
|
|
1628
|
+
handler(id) {
|
|
1629
|
+
if (id.startsWith("/@slidev/") || id.includes("__slidev_")) return id;
|
|
1637
1630
|
return null;
|
|
1638
1631
|
}
|
|
1639
1632
|
},
|
|
1640
|
-
async load(id
|
|
1641
|
-
const template = templates.find((i) => i.id === id
|
|
1642
|
-
if (template)
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1633
|
+
async load(id) {
|
|
1634
|
+
const template = templates.find((i) => i.id === id);
|
|
1635
|
+
if (template) {
|
|
1636
|
+
const templateContext = {
|
|
1637
|
+
resolve: this.resolve.bind(this),
|
|
1638
|
+
makeAbsoluteImportGlob
|
|
1639
|
+
};
|
|
1640
|
+
return {
|
|
1641
|
+
code: await template.getContent.call(templateContext, options),
|
|
1642
|
+
map: { mappings: "" }
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
const matchFacade = id.match(regexSlideFacadeId);
|
|
1647
1646
|
if (matchFacade) {
|
|
1648
1647
|
const [, no, type] = matchFacade;
|
|
1649
1648
|
const idx = +no - 1;
|
|
1650
1649
|
const sourceId = JSON.stringify(sourceIds[type][idx]);
|
|
1651
1650
|
return [`export * from ${sourceId}`, `export { default } from ${sourceId}`].join("\n");
|
|
1652
1651
|
}
|
|
1653
|
-
const matchSource = id
|
|
1652
|
+
const matchSource = id.match(regexSlideSourceId);
|
|
1654
1653
|
if (matchSource) {
|
|
1655
1654
|
const [, no, type] = matchSource;
|
|
1656
1655
|
const idx = +no - 1;
|
|
@@ -1666,6 +1665,7 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1666
1665
|
frontmatter: void 0,
|
|
1667
1666
|
source: void 0,
|
|
1668
1667
|
importChain: void 0,
|
|
1668
|
+
images: slide.images ?? slide.source?.images,
|
|
1669
1669
|
...mode === "build" ? {
|
|
1670
1670
|
raw: "",
|
|
1671
1671
|
content: "",
|
|
@@ -1702,6 +1702,7 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1702
1702
|
` frontmatter,`,
|
|
1703
1703
|
` filepath: ${JSON.stringify(mode === "dev" ? slide.source.filepath : "")},`,
|
|
1704
1704
|
` start: ${JSON.stringify(slide.source.start)},`,
|
|
1705
|
+
` sourceIndex: ${JSON.stringify(slide.source.index)},`,
|
|
1705
1706
|
` id: ${idx},`,
|
|
1706
1707
|
` no: ${no},`,
|
|
1707
1708
|
" },",
|
|
@@ -1713,7 +1714,7 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1713
1714
|
};
|
|
1714
1715
|
}
|
|
1715
1716
|
}
|
|
1716
|
-
if (data.markdownFiles[id
|
|
1717
|
+
if (data.markdownFiles[id]) return "";
|
|
1717
1718
|
}
|
|
1718
1719
|
};
|
|
1719
1720
|
function renderNote(text = "") {
|
|
@@ -1734,15 +1735,14 @@ function createSlidesLoader(options, serverOptions) {
|
|
|
1734
1735
|
}
|
|
1735
1736
|
return notesMd.render(md);
|
|
1736
1737
|
}
|
|
1737
|
-
function withRenderedNote(data
|
|
1738
|
+
function withRenderedNote(data) {
|
|
1738
1739
|
return {
|
|
1739
|
-
...data
|
|
1740
|
+
...data,
|
|
1740
1741
|
...withoutNotes && { note: "" },
|
|
1741
|
-
noteHTML: renderNote(data
|
|
1742
|
+
noteHTML: renderNote(data?.note)
|
|
1742
1743
|
};
|
|
1743
1744
|
}
|
|
1744
1745
|
}
|
|
1745
|
-
|
|
1746
1746
|
//#endregion
|
|
1747
1747
|
//#region node/setups/transformers.ts
|
|
1748
1748
|
async function setupTransformers(roots) {
|
|
@@ -1765,26 +1765,24 @@ async function setupTransformers(roots) {
|
|
|
1765
1765
|
}
|
|
1766
1766
|
return result;
|
|
1767
1767
|
}
|
|
1768
|
-
|
|
1769
1768
|
//#endregion
|
|
1770
|
-
//#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=
|
|
1769
|
+
//#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=49e14003b6caa0b7d164cbe71da573809d375bab_20f9b6719589445333fe4d166d07e531/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
|
|
1771
1770
|
var SpecialCharacters;
|
|
1772
|
-
(function(SpecialCharacters
|
|
1773
|
-
SpecialCharacters
|
|
1774
|
-
SpecialCharacters
|
|
1775
|
-
SpecialCharacters
|
|
1776
|
-
SpecialCharacters
|
|
1777
|
-
SpecialCharacters
|
|
1778
|
-
SpecialCharacters
|
|
1779
|
-
SpecialCharacters
|
|
1780
|
-
SpecialCharacters
|
|
1781
|
-
SpecialCharacters
|
|
1782
|
-
SpecialCharacters
|
|
1783
|
-
SpecialCharacters
|
|
1771
|
+
(function(SpecialCharacters) {
|
|
1772
|
+
SpecialCharacters[SpecialCharacters["EXCLAMATION_MARK"] = 33] = "EXCLAMATION_MARK";
|
|
1773
|
+
SpecialCharacters[SpecialCharacters["OPENING_BRACKET"] = 91] = "OPENING_BRACKET";
|
|
1774
|
+
SpecialCharacters[SpecialCharacters["OPENING_PARENTHESIS"] = 40] = "OPENING_PARENTHESIS";
|
|
1775
|
+
SpecialCharacters[SpecialCharacters["WHITESPACE"] = 32] = "WHITESPACE";
|
|
1776
|
+
SpecialCharacters[SpecialCharacters["NEW_LINE"] = 10] = "NEW_LINE";
|
|
1777
|
+
SpecialCharacters[SpecialCharacters["EQUALS"] = 61] = "EQUALS";
|
|
1778
|
+
SpecialCharacters[SpecialCharacters["LOWER_CASE_X"] = 120] = "LOWER_CASE_X";
|
|
1779
|
+
SpecialCharacters[SpecialCharacters["NUMBER_ZERO"] = 48] = "NUMBER_ZERO";
|
|
1780
|
+
SpecialCharacters[SpecialCharacters["NUMBER_NINE"] = 57] = "NUMBER_NINE";
|
|
1781
|
+
SpecialCharacters[SpecialCharacters["PERCENTAGE"] = 37] = "PERCENTAGE";
|
|
1782
|
+
SpecialCharacters[SpecialCharacters["CLOSING_PARENTHESIS"] = 41] = "CLOSING_PARENTHESIS";
|
|
1784
1783
|
})(SpecialCharacters || (SpecialCharacters = {}));
|
|
1785
|
-
|
|
1786
1784
|
//#endregion
|
|
1787
|
-
//#region ../../node_modules/.pnpm/markdown-it@14.
|
|
1785
|
+
//#region ../../node_modules/.pnpm/markdown-it@14.2.0/node_modules/markdown-it/lib/token.mjs
|
|
1788
1786
|
/**
|
|
1789
1787
|
* class Token
|
|
1790
1788
|
**/
|
|
@@ -1937,10 +1935,8 @@ Token.prototype.attrJoin = function attrJoin(name, value) {
|
|
|
1937
1935
|
if (idx < 0) this.attrPush([name, value]);
|
|
1938
1936
|
else this.attrs[idx][1] = this.attrs[idx][1] + " " + value;
|
|
1939
1937
|
};
|
|
1940
|
-
var token_default = Token;
|
|
1941
|
-
|
|
1942
1938
|
//#endregion
|
|
1943
|
-
//#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=
|
|
1939
|
+
//#region ../../node_modules/.pnpm/@hedgedoc+markdown-it-plugins@2.1.4_patch_hash=49e14003b6caa0b7d164cbe71da573809d375bab_20f9b6719589445333fe4d166d07e531/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/task-lists/index.js
|
|
1944
1940
|
const checkboxRegex = /^ *\[([\sx])] /i;
|
|
1945
1941
|
function taskLists(md, options = {
|
|
1946
1942
|
enabled: false,
|
|
@@ -1985,11 +1981,11 @@ function isTodoItem(tokens, index) {
|
|
|
1985
1981
|
}
|
|
1986
1982
|
function todoify(token, options) {
|
|
1987
1983
|
if (token.children == null) return;
|
|
1988
|
-
const id
|
|
1989
|
-
token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id
|
|
1984
|
+
const id = generateIdForToken(token);
|
|
1985
|
+
token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id));
|
|
1990
1986
|
token.children[1].content = token.children[1].content.replace(checkboxRegex, "");
|
|
1991
1987
|
if (options.label) {
|
|
1992
|
-
token.children.splice(1, 0, createLabelBeginToken(id
|
|
1988
|
+
token.children.splice(1, 0, createLabelBeginToken(id));
|
|
1993
1989
|
token.children.push(createLabelEndToken());
|
|
1994
1990
|
}
|
|
1995
1991
|
}
|
|
@@ -1997,21 +1993,21 @@ function generateIdForToken(token) {
|
|
|
1997
1993
|
if (token.map) return `task-item-${token.map[0]}`;
|
|
1998
1994
|
else return `task-item-${Math.ceil(Math.random() * (1e4 * 1e3) - 1e3)}`;
|
|
1999
1995
|
}
|
|
2000
|
-
function createCheckboxToken(token, enabled, id
|
|
2001
|
-
const checkbox = new
|
|
1996
|
+
function createCheckboxToken(token, enabled, id) {
|
|
1997
|
+
const checkbox = new Token("taskListItemCheckbox", "", 0);
|
|
2002
1998
|
if (!enabled) checkbox.attrSet("disabled", "true");
|
|
2003
1999
|
if (token.map) checkbox.attrSet("line", token.map[0].toString());
|
|
2004
|
-
checkbox.attrSet("id", id
|
|
2000
|
+
checkbox.attrSet("id", id);
|
|
2005
2001
|
if (checkboxRegex.exec(token.content)?.[1].toLowerCase() === "x") checkbox.attrSet("checked", "true");
|
|
2006
2002
|
return checkbox;
|
|
2007
2003
|
}
|
|
2008
|
-
function createLabelBeginToken(id
|
|
2009
|
-
const labelBeginToken = new
|
|
2010
|
-
labelBeginToken.attrSet("id", id
|
|
2004
|
+
function createLabelBeginToken(id) {
|
|
2005
|
+
const labelBeginToken = new Token("taskListItemLabel_open", "", 1);
|
|
2006
|
+
labelBeginToken.attrSet("id", id);
|
|
2011
2007
|
return labelBeginToken;
|
|
2012
2008
|
}
|
|
2013
2009
|
function createLabelEndToken() {
|
|
2014
|
-
return new
|
|
2010
|
+
return new Token("taskListItemLabel_close", "", -1);
|
|
2015
2011
|
}
|
|
2016
2012
|
function isInline(token) {
|
|
2017
2013
|
return token.type === "inline";
|
|
@@ -2025,38 +2021,227 @@ function isListItem(token) {
|
|
|
2025
2021
|
function startsWithTodoMarkdown(token) {
|
|
2026
2022
|
return checkboxRegex.test(token.content);
|
|
2027
2023
|
}
|
|
2028
|
-
|
|
2024
|
+
//#endregion
|
|
2025
|
+
//#region node/vite/monacoWrite.ts
|
|
2026
|
+
const monacoWriterWhitelist = /* @__PURE__ */ new Set();
|
|
2027
|
+
function createMonacoWriterPlugin({ userRoot }) {
|
|
2028
|
+
return {
|
|
2029
|
+
name: "slidev:monaco-write",
|
|
2030
|
+
apply: "serve",
|
|
2031
|
+
configureServer(server) {
|
|
2032
|
+
server.ws.on("connection", (socket) => {
|
|
2033
|
+
socket.on("message", async (data) => {
|
|
2034
|
+
let json;
|
|
2035
|
+
try {
|
|
2036
|
+
json = JSON.parse(data.toString());
|
|
2037
|
+
} catch {
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
if (json.type === "custom" && json.event === "slidev:monaco-write") {
|
|
2041
|
+
const { file, content } = json.data;
|
|
2042
|
+
if (!monacoWriterWhitelist.has(file)) {
|
|
2043
|
+
console.error(`[Slidev] Unauthorized file write: ${file}`);
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
const filepath = path.join(userRoot, file);
|
|
2047
|
+
console.log("[Slidev] Writing file:", filepath);
|
|
2048
|
+
await fs$1.writeFile(filepath, content, "utf-8");
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
//#endregion
|
|
2056
|
+
//#region node/syntax/snippet.ts
|
|
2057
|
+
const RE_NEWLINE = /\r?\n/;
|
|
2058
|
+
function dedent(text) {
|
|
2059
|
+
const lines = text.split("\n");
|
|
2060
|
+
const minIndentLength = lines.reduce((acc, line) => {
|
|
2061
|
+
for (let i = 0; i < line.length; i++) if (line[i] !== " " && line[i] !== " ") return Math.min(i, acc);
|
|
2062
|
+
return acc;
|
|
2063
|
+
}, Number.POSITIVE_INFINITY);
|
|
2064
|
+
if (minIndentLength < Number.POSITIVE_INFINITY) return lines.map((x) => x.slice(minIndentLength)).join("\n");
|
|
2065
|
+
return text;
|
|
2066
|
+
}
|
|
2067
|
+
const markers = [
|
|
2068
|
+
{
|
|
2069
|
+
start: /^\s*\/\/\s*#?region\b\s*(.*?)\s*$/,
|
|
2070
|
+
end: /^\s*\/\/\s*#?endregion\b\s*(.*?)\s*$/
|
|
2071
|
+
},
|
|
2072
|
+
{
|
|
2073
|
+
start: /^\s*<!--\s*#?region\b\s*(.*?)\s*-->/,
|
|
2074
|
+
end: /^\s*<!--\s*#?endregion\b\s*(.*?)\s*-->/
|
|
2075
|
+
},
|
|
2076
|
+
{
|
|
2077
|
+
start: /^\s*\/\*\s*#region\b\s*(.*?)\s*\*\//,
|
|
2078
|
+
end: /^\s*\/\*\s*#endregion\b\s*(.*?)\s*\*\//
|
|
2079
|
+
},
|
|
2080
|
+
{
|
|
2081
|
+
start: /^\s*#[rR]egion\b\s*(.*?)\s*$/,
|
|
2082
|
+
end: /^\s*#[eE]nd ?[rR]egion\b\s*(.*?)\s*$/
|
|
2083
|
+
},
|
|
2084
|
+
{
|
|
2085
|
+
start: /^\s*#\s*#?region\b\s*(.*?)\s*$/,
|
|
2086
|
+
end: /^\s*#\s*#?endregion\b\s*(.*?)\s*$/
|
|
2087
|
+
},
|
|
2088
|
+
{
|
|
2089
|
+
start: /^\s*(?:--|::|@?REM)\s*#region\b\s*(.*?)\s*$/,
|
|
2090
|
+
end: /^\s*(?:--|::|@?REM)\s*#endregion\b\s*(.*?)\s*$/
|
|
2091
|
+
},
|
|
2092
|
+
{
|
|
2093
|
+
start: /^\s*#pragma\s+region\b\s*(.*?)\s*$/,
|
|
2094
|
+
end: /^\s*#pragma\s+endregion\b\s*(.*?)\s*$/
|
|
2095
|
+
},
|
|
2096
|
+
{
|
|
2097
|
+
start: /^\s*\(\*\s*#region\b\s*(.*?)\s*\*\)/,
|
|
2098
|
+
end: /^\s*\(\*\s*#endregion\b\s*(.*?)\s*\*\)/
|
|
2099
|
+
}
|
|
2100
|
+
];
|
|
2101
|
+
function findRegion(lines, regionName) {
|
|
2102
|
+
let chosen = null;
|
|
2103
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2104
|
+
for (const re of markers) if (re.start.exec(lines[i])?.[1] === regionName) {
|
|
2105
|
+
chosen = {
|
|
2106
|
+
re,
|
|
2107
|
+
start: i + 1
|
|
2108
|
+
};
|
|
2109
|
+
break;
|
|
2110
|
+
}
|
|
2111
|
+
if (chosen) break;
|
|
2112
|
+
}
|
|
2113
|
+
if (!chosen) return null;
|
|
2114
|
+
let counter = 1;
|
|
2115
|
+
for (let i = chosen.start; i < lines.length; i++) {
|
|
2116
|
+
if (chosen.re.start.exec(lines[i])?.[1] === regionName) {
|
|
2117
|
+
counter++;
|
|
2118
|
+
continue;
|
|
2119
|
+
}
|
|
2120
|
+
const endRegion = chosen.re.end.exec(lines[i])?.[1];
|
|
2121
|
+
if (endRegion === regionName || endRegion === "") {
|
|
2122
|
+
if (--counter === 0) return {
|
|
2123
|
+
...chosen,
|
|
2124
|
+
end: i
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return null;
|
|
2129
|
+
}
|
|
2130
|
+
const RE_SNIPPET_IMPORT = /^<<<[ \t]*(\S.*?)(#[\w-]+)?[ \t]*(?:[ \t](\S+?))?[ \t]*(\{.*)?$/;
|
|
2131
|
+
function resolveSnippetImport(lineText, userRoot, slide) {
|
|
2132
|
+
const match = lineText.trimStart().match(RE_SNIPPET_IMPORT);
|
|
2133
|
+
if (!match) return null;
|
|
2134
|
+
let [, filepath = "", regionName = "", lang = "", meta = ""] = match;
|
|
2135
|
+
const dir = path.dirname(slide.source.filepath);
|
|
2136
|
+
const src = slash(filepath.startsWith("@/") ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
|
|
2137
|
+
lang = lang.trim() || path.extname(filepath).slice(1);
|
|
2138
|
+
meta = meta.trim();
|
|
2139
|
+
if (!(fs.existsSync(src) && fs.statSync(src).isFile())) throw new Error(`Code snippet path not found: ${src}`);
|
|
2140
|
+
let content = fs.readFileSync(src, "utf8");
|
|
2141
|
+
if (regionName) {
|
|
2142
|
+
const lines = content.split(RE_NEWLINE);
|
|
2143
|
+
const region = findRegion(lines, regionName.slice(1));
|
|
2144
|
+
if (region) content = dedent(lines.slice(region.start, region.end).filter((l) => !(region.re.start.test(l) || region.re.end.test(l))).join("\n"));
|
|
2145
|
+
}
|
|
2146
|
+
return {
|
|
2147
|
+
content,
|
|
2148
|
+
filepath,
|
|
2149
|
+
lang,
|
|
2150
|
+
meta,
|
|
2151
|
+
src
|
|
2152
|
+
};
|
|
2153
|
+
}
|
|
2154
|
+
function MarkdownItSnippet(md, { userRoot, data: { watchFiles, slides } }) {
|
|
2155
|
+
md.block.ruler.before("fence", "snippet_import", (state, startLine, _endLine, silent) => {
|
|
2156
|
+
const pos = state.bMarks[startLine] + state.tShift[startLine];
|
|
2157
|
+
const max = state.eMarks[startLine];
|
|
2158
|
+
const lineText = state.src.slice(pos, max);
|
|
2159
|
+
if (!lineText.match(RE_SNIPPET_IMPORT)) return false;
|
|
2160
|
+
if (silent) return true;
|
|
2161
|
+
const slideNo = state.env.id?.match(regexSlideSourceId);
|
|
2162
|
+
const slide = slideNo ? slides[slideNo[1] - 1] : null;
|
|
2163
|
+
if (!slide) {
|
|
2164
|
+
console.warn(yellow(`[markdown-it-snippet] Snippet syntax is not supported in ${state.env.id || "unknown source"}. Skipped.`));
|
|
2165
|
+
return false;
|
|
2166
|
+
}
|
|
2167
|
+
const snippet = resolveSnippetImport(lineText, userRoot, slide);
|
|
2168
|
+
if (!snippet) return false;
|
|
2169
|
+
const { content, filepath, src } = snippet;
|
|
2170
|
+
let { lang, meta } = snippet;
|
|
2171
|
+
if (meta.includes("{monaco-write}")) {
|
|
2172
|
+
monacoWriterWhitelist.add(filepath);
|
|
2173
|
+
lang = lang.trim();
|
|
2174
|
+
meta = meta.replace("{monaco-write}", "").trim() || "{}";
|
|
2175
|
+
const safeFilepath = JSON.stringify(filepath).slice(1, -1);
|
|
2176
|
+
const encoded = lz.compressToBase64(content);
|
|
2177
|
+
const token = state.push("html_block", "", 0);
|
|
2178
|
+
token.content = `<Monaco writable="${safeFilepath}" code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />\n`;
|
|
2179
|
+
token.map = [startLine, startLine + 1];
|
|
2180
|
+
} else {
|
|
2181
|
+
watchFiles[src] ??= /* @__PURE__ */ new Set();
|
|
2182
|
+
watchFiles[src].add(slide.index);
|
|
2183
|
+
const token = state.push("fence", "code", 0);
|
|
2184
|
+
token.info = `${lang} ${meta}`.trim();
|
|
2185
|
+
token.content = content.endsWith("\n") ? content : `${content}\n`;
|
|
2186
|
+
token.map = [startLine, startLine + 1];
|
|
2187
|
+
}
|
|
2188
|
+
state.line = startLine + 1;
|
|
2189
|
+
return true;
|
|
2190
|
+
}, { alt: [
|
|
2191
|
+
"paragraph",
|
|
2192
|
+
"reference",
|
|
2193
|
+
"blockquote",
|
|
2194
|
+
"list"
|
|
2195
|
+
] });
|
|
2196
|
+
}
|
|
2029
2197
|
//#endregion
|
|
2030
2198
|
//#region node/syntax/codeblock/magic-move.ts
|
|
2031
2199
|
const RE_MAGIC_MOVE_INFO = /^(?:md|markdown) magic-move\s*(?:\[([^\]]*)\])?\s*(\{[^}]*\})?/;
|
|
2032
2200
|
const RE_CODE_BLOCK = /^```([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w*,|-]+)\}[ \t]*(\{[^}]*\})?([^\r\n]*))?\r?\n((?:(?!^```)[\s\S])*?)^```$/gm;
|
|
2201
|
+
const RE_INNER_CODE_FENCE = /^```/;
|
|
2033
2202
|
const RE_LINES_TRUE = /\blines: *true\b/;
|
|
2034
2203
|
const RE_LINES_FALSE = /\blines: *false\b/;
|
|
2035
2204
|
function parseLineNumbersOption(options) {
|
|
2036
2205
|
return RE_LINES_TRUE.test(options) ? true : RE_LINES_FALSE.test(options) ? false : void 0;
|
|
2037
2206
|
}
|
|
2038
|
-
|
|
2207
|
+
function resolveMagicMoveSnippetImports(code, userRoot, slide, watchFiles) {
|
|
2208
|
+
let inCodeBlock = false;
|
|
2209
|
+
return code.split(/\r?\n/).map((line) => {
|
|
2210
|
+
if (RE_INNER_CODE_FENCE.test(line)) {
|
|
2211
|
+
inCodeBlock = !inCodeBlock;
|
|
2212
|
+
return line;
|
|
2213
|
+
}
|
|
2214
|
+
if (inCodeBlock) return line;
|
|
2215
|
+
const snippet = resolveSnippetImport(line, userRoot, slide);
|
|
2216
|
+
if (!snippet) return line;
|
|
2217
|
+
watchFiles[snippet.src] ??= /* @__PURE__ */ new Set();
|
|
2218
|
+
watchFiles[snippet.src].add(slide.index);
|
|
2219
|
+
return `\`\`\`${`${snippet.lang} ${snippet.meta}`.trim()}\n${snippet.content.endsWith("\n") ? snippet.content : `${snippet.content}\n`}\`\`\``;
|
|
2220
|
+
}).join("\n");
|
|
2221
|
+
}
|
|
2222
|
+
var magic_move_default = defineCodeblockTransformer(async ({ info, fence, code, slide, options: { userRoot, data: { config, watchFiles }, utils: { shikiOptions, shiki } } }) => {
|
|
2039
2223
|
if (fence !== 4) return;
|
|
2040
2224
|
const match = info.match(RE_MAGIC_MOVE_INFO);
|
|
2041
2225
|
if (!match) return;
|
|
2042
2226
|
const [, title = "", options = "{}"] = match;
|
|
2043
2227
|
const defaultLineNumbers = parseLineNumbersOption(options) ?? config.lineNumbers;
|
|
2044
|
-
const
|
|
2228
|
+
const resolvedCode = slide ? resolveMagicMoveSnippetImports(code, userRoot, slide, watchFiles) : code;
|
|
2229
|
+
const matches = Array.from(resolvedCode.matchAll(RE_CODE_BLOCK));
|
|
2045
2230
|
if (!matches.length) throw new Error("Magic Move block must contain at least one code block");
|
|
2046
2231
|
const ranges = matches.map((i) => normalizeRangeStr(i[3]));
|
|
2047
2232
|
const steps = await Promise.all(matches.map(async (i) => {
|
|
2048
2233
|
const lang = i[1];
|
|
2049
2234
|
const lineNumbers = parseLineNumbersOption(i[4]) ?? defaultLineNumbers;
|
|
2050
|
-
const code
|
|
2051
|
-
const options
|
|
2235
|
+
const code = i[6].trimEnd();
|
|
2236
|
+
const options = {
|
|
2052
2237
|
...shikiOptions,
|
|
2053
2238
|
lang
|
|
2054
2239
|
};
|
|
2055
|
-
const { tokens, bg, fg
|
|
2240
|
+
const { tokens, bg, fg, rootStyle, themeName } = await shiki.codeToTokens(code, options);
|
|
2056
2241
|
return {
|
|
2057
|
-
...toKeyedTokens(code
|
|
2242
|
+
...toKeyedTokens(code, tokens, JSON.stringify([lang, "themes" in options ? options.themes : options.theme]), lineNumbers),
|
|
2058
2243
|
bg,
|
|
2059
|
-
fg
|
|
2244
|
+
fg,
|
|
2060
2245
|
rootStyle,
|
|
2061
2246
|
themeName,
|
|
2062
2247
|
lang
|
|
@@ -2064,7 +2249,6 @@ var magic_move_default = defineCodeblockTransformer(async ({ info, fence, code,
|
|
|
2064
2249
|
}));
|
|
2065
2250
|
return `<ShikiMagicMove v-bind="${options}" steps-lz="${lz.compressToBase64(JSON.stringify(steps))}" :title='${JSON.stringify(title)}' :step-ranges='${JSON.stringify(ranges)}' />`;
|
|
2066
2251
|
});
|
|
2067
|
-
|
|
2068
2252
|
//#endregion
|
|
2069
2253
|
//#region node/syntax/codeblock/mermaid.ts
|
|
2070
2254
|
const RE_MERMAID = /^mermaid\s*(\{[^\n]*\})?/;
|
|
@@ -2074,7 +2258,6 @@ var mermaid_default = defineCodeblockTransformer(async ({ info, code }) => {
|
|
|
2074
2258
|
const [, options] = match;
|
|
2075
2259
|
return `<Mermaid ${options ? `v-bind="${options}"` : ""} code-lz="${lz.compressToBase64(code.trim())}" />`;
|
|
2076
2260
|
});
|
|
2077
|
-
|
|
2078
2261
|
//#endregion
|
|
2079
2262
|
//#region node/syntax/codeblock/monaco.ts
|
|
2080
2263
|
const RE_MONACO = /^([\w'-]+)?\s*\{(monaco[\w-]*)\}\s*(\{[^}]*\})?(.*)$/;
|
|
@@ -2093,7 +2276,6 @@ var monaco_default = defineCodeblockTransformer(async ({ info, code, options: {
|
|
|
2093
2276
|
} else encoded = lz.compressToBase64(code);
|
|
2094
2277
|
return `<Monaco ${options ? `v-bind="${options}"` : ""} ${monaco === "monaco-run" ? "runnable" : ""} lang="${lang}" code-lz="${encoded}" ${diff} />`;
|
|
2095
2278
|
});
|
|
2096
|
-
|
|
2097
2279
|
//#endregion
|
|
2098
2280
|
//#region node/syntax/codeblock/plant-uml.ts
|
|
2099
2281
|
const RE_PLANT_UML = /^plantuml\s*(\{[^\n]*\})?/;
|
|
@@ -2103,7 +2285,6 @@ var plant_uml_default = defineCodeblockTransformer(async ({ info, code, options:
|
|
|
2103
2285
|
const [, options] = match;
|
|
2104
2286
|
return `<PlantUml ${options ? `v-bind="${options}"` : ""} code="${encode(code.trim())}"${plantUmlServer === void 0 ? "" : ` server=${JSON.stringify(plantUmlServer)}`} />`;
|
|
2105
2287
|
});
|
|
2106
|
-
|
|
2107
2288
|
//#endregion
|
|
2108
2289
|
//#region node/syntax/codeblock/wrapper.ts
|
|
2109
2290
|
const RE_BLOCK_INFO = /^([\w'-]+)?(?:[ \t]*|[ \t][ \w\t'-]*)(?:\[([^\]]*)\])?[ \t]*(?:\{([\w,|\-*]+)\})?[ \t]*(\{[^}]*\})?(.*)$/;
|
|
@@ -2114,7 +2295,6 @@ var wrapper_default = defineCodeblockTransformer(async ({ info, renderHighlighte
|
|
|
2114
2295
|
const code = await renderHighlighted({ info: `${lang} ${rest}` });
|
|
2115
2296
|
return `<CodeBlockWrapper ${optionsProp} title=${JSON.stringify(title)} :ranges='${JSON.stringify(ranges)}'>${escapeVueInCode(code)}</CodeBlockWrapper>`;
|
|
2116
2297
|
});
|
|
2117
|
-
|
|
2118
2298
|
//#endregion
|
|
2119
2299
|
//#region node/syntax/codeblock/index.ts
|
|
2120
2300
|
function MarkdownItCodeblocks(md, options, extraTransformers) {
|
|
@@ -2150,7 +2330,6 @@ function MarkdownItCodeblocks(md, options, extraTransformers) {
|
|
|
2150
2330
|
throw new Error("Should not reach here");
|
|
2151
2331
|
};
|
|
2152
2332
|
}
|
|
2153
|
-
|
|
2154
2333
|
//#endregion
|
|
2155
2334
|
//#region node/syntax/drag.ts
|
|
2156
2335
|
const dragComponentRegex = /<(v-?drag-?\w*)([\s>])/i;
|
|
@@ -2158,8 +2337,8 @@ const dragDirectiveRegex = /(?<![</\w])v-drag(=".*?")?/i;
|
|
|
2158
2337
|
function MarkdownItVDrag(md, markdownTransformMap) {
|
|
2159
2338
|
const visited = /* @__PURE__ */ new WeakSet();
|
|
2160
2339
|
const sourceMapConsumers = /* @__PURE__ */ new WeakMap();
|
|
2161
|
-
function getSourceMapConsumer(id
|
|
2162
|
-
const s = id
|
|
2340
|
+
function getSourceMapConsumer(id) {
|
|
2341
|
+
const s = id && markdownTransformMap.get(id);
|
|
2163
2342
|
if (!s) return void 0;
|
|
2164
2343
|
let smc = sourceMapConsumers.get(s);
|
|
2165
2344
|
if (smc) return smc;
|
|
@@ -2209,7 +2388,6 @@ function MarkdownItVDrag(md, markdownTransformMap) {
|
|
|
2209
2388
|
});
|
|
2210
2389
|
};
|
|
2211
2390
|
}
|
|
2212
|
-
|
|
2213
2391
|
//#endregion
|
|
2214
2392
|
//#region node/syntax/escape-code.ts
|
|
2215
2393
|
const RE_CODE_TAG_OPEN = /^<code/;
|
|
@@ -2219,7 +2397,6 @@ function MarkdownItEscapeInlineCode(md) {
|
|
|
2219
2397
|
return (await codeInline(tokens, idx, options, env, self)).replace(RE_CODE_TAG_OPEN, "<code v-pre");
|
|
2220
2398
|
};
|
|
2221
2399
|
}
|
|
2222
|
-
|
|
2223
2400
|
//#endregion
|
|
2224
2401
|
//#region node/syntax/scoped.ts
|
|
2225
2402
|
const RE_STYLE_TAG_OPEN = /<style\b([^>]*)>/gi;
|
|
@@ -2236,7 +2413,6 @@ function MarkdownItStyleScoped(md) {
|
|
|
2236
2413
|
const htmlInline = md.renderer.rules.html_inline;
|
|
2237
2414
|
md.renderer.rules.html_inline = async (...args) => addScoped(await htmlInline(...args));
|
|
2238
2415
|
}
|
|
2239
|
-
|
|
2240
2416
|
//#endregion
|
|
2241
2417
|
//#region node/syntax/shiki.ts
|
|
2242
2418
|
async function MarkdownItShiki({ data: { config }, mode, utils: { shiki, shikiOptions } }) {
|
|
@@ -2254,7 +2430,10 @@ async function MarkdownItShiki({ data: { config }, mode, utils: { shiki, shikiOp
|
|
|
2254
2430
|
]);
|
|
2255
2431
|
return transformerTwoslash({
|
|
2256
2432
|
explicitTrigger: true,
|
|
2257
|
-
twoslashOptions: {
|
|
2433
|
+
twoslashOptions: {
|
|
2434
|
+
compilerOptions: { ignoreDeprecations: "6.0" },
|
|
2435
|
+
handbookOptions: { noErrorValidation: true }
|
|
2436
|
+
}
|
|
2258
2437
|
});
|
|
2259
2438
|
}
|
|
2260
2439
|
const transformers = [
|
|
@@ -2281,7 +2460,6 @@ function transformerTwoslashConditional() {
|
|
|
2281
2460
|
}
|
|
2282
2461
|
};
|
|
2283
2462
|
}
|
|
2284
|
-
|
|
2285
2463
|
//#endregion
|
|
2286
2464
|
//#region node/syntax/slot-sugar.ts
|
|
2287
2465
|
const RE_SLOT_MARKER = /^::\s*([\w.\-:]+)\s*::\s*$/;
|
|
@@ -2332,167 +2510,6 @@ function MarkdownItSlotSugar(md) {
|
|
|
2332
2510
|
state.tokens = newTokens;
|
|
2333
2511
|
});
|
|
2334
2512
|
}
|
|
2335
|
-
|
|
2336
|
-
//#endregion
|
|
2337
|
-
//#region node/vite/monacoWrite.ts
|
|
2338
|
-
const monacoWriterWhitelist = /* @__PURE__ */ new Set();
|
|
2339
|
-
function createMonacoWriterPlugin({ userRoot }) {
|
|
2340
|
-
return {
|
|
2341
|
-
name: "slidev:monaco-write",
|
|
2342
|
-
apply: "serve",
|
|
2343
|
-
configureServer(server) {
|
|
2344
|
-
server.ws.on("connection", (socket) => {
|
|
2345
|
-
socket.on("message", async (data) => {
|
|
2346
|
-
let json;
|
|
2347
|
-
try {
|
|
2348
|
-
json = JSON.parse(data.toString());
|
|
2349
|
-
} catch {
|
|
2350
|
-
return;
|
|
2351
|
-
}
|
|
2352
|
-
if (json.type === "custom" && json.event === "slidev:monaco-write") {
|
|
2353
|
-
const { file, content } = json.data;
|
|
2354
|
-
if (!monacoWriterWhitelist.has(file)) {
|
|
2355
|
-
console.error(`[Slidev] Unauthorized file write: ${file}`);
|
|
2356
|
-
return;
|
|
2357
|
-
}
|
|
2358
|
-
const filepath = path.join(userRoot, file);
|
|
2359
|
-
console.log("[Slidev] Writing file:", filepath);
|
|
2360
|
-
await fs$1.writeFile(filepath, content, "utf-8");
|
|
2361
|
-
}
|
|
2362
|
-
});
|
|
2363
|
-
});
|
|
2364
|
-
}
|
|
2365
|
-
};
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
//#endregion
|
|
2369
|
-
//#region node/syntax/snippet.ts
|
|
2370
|
-
const RE_NEWLINE = /\r?\n/;
|
|
2371
|
-
function dedent(text) {
|
|
2372
|
-
const lines = text.split("\n");
|
|
2373
|
-
const minIndentLength = lines.reduce((acc, line) => {
|
|
2374
|
-
for (let i = 0; i < line.length; i++) if (line[i] !== " " && line[i] !== " ") return Math.min(i, acc);
|
|
2375
|
-
return acc;
|
|
2376
|
-
}, Number.POSITIVE_INFINITY);
|
|
2377
|
-
if (minIndentLength < Number.POSITIVE_INFINITY) return lines.map((x) => x.slice(minIndentLength)).join("\n");
|
|
2378
|
-
return text;
|
|
2379
|
-
}
|
|
2380
|
-
const markers = [
|
|
2381
|
-
{
|
|
2382
|
-
start: /^\s*\/\/\s*#?region\b\s*(.*?)\s*$/,
|
|
2383
|
-
end: /^\s*\/\/\s*#?endregion\b\s*(.*?)\s*$/
|
|
2384
|
-
},
|
|
2385
|
-
{
|
|
2386
|
-
start: /^\s*<!--\s*#?region\b\s*(.*?)\s*-->/,
|
|
2387
|
-
end: /^\s*<!--\s*#?endregion\b\s*(.*?)\s*-->/
|
|
2388
|
-
},
|
|
2389
|
-
{
|
|
2390
|
-
start: /^\s*\/\*\s*#region\b\s*(.*?)\s*\*\//,
|
|
2391
|
-
end: /^\s*\/\*\s*#endregion\b\s*(.*?)\s*\*\//
|
|
2392
|
-
},
|
|
2393
|
-
{
|
|
2394
|
-
start: /^\s*#[rR]egion\b\s*(.*?)\s*$/,
|
|
2395
|
-
end: /^\s*#[eE]nd ?[rR]egion\b\s*(.*?)\s*$/
|
|
2396
|
-
},
|
|
2397
|
-
{
|
|
2398
|
-
start: /^\s*#\s*#?region\b\s*(.*?)\s*$/,
|
|
2399
|
-
end: /^\s*#\s*#?endregion\b\s*(.*?)\s*$/
|
|
2400
|
-
},
|
|
2401
|
-
{
|
|
2402
|
-
start: /^\s*(?:--|::|@?REM)\s*#region\b\s*(.*?)\s*$/,
|
|
2403
|
-
end: /^\s*(?:--|::|@?REM)\s*#endregion\b\s*(.*?)\s*$/
|
|
2404
|
-
},
|
|
2405
|
-
{
|
|
2406
|
-
start: /^\s*#pragma\s+region\b\s*(.*?)\s*$/,
|
|
2407
|
-
end: /^\s*#pragma\s+endregion\b\s*(.*?)\s*$/
|
|
2408
|
-
},
|
|
2409
|
-
{
|
|
2410
|
-
start: /^\s*\(\*\s*#region\b\s*(.*?)\s*\*\)/,
|
|
2411
|
-
end: /^\s*\(\*\s*#endregion\b\s*(.*?)\s*\*\)/
|
|
2412
|
-
}
|
|
2413
|
-
];
|
|
2414
|
-
function findRegion(lines, regionName) {
|
|
2415
|
-
let chosen = null;
|
|
2416
|
-
for (let i = 0; i < lines.length; i++) {
|
|
2417
|
-
for (const re of markers) if (re.start.exec(lines[i])?.[1] === regionName) {
|
|
2418
|
-
chosen = {
|
|
2419
|
-
re,
|
|
2420
|
-
start: i + 1
|
|
2421
|
-
};
|
|
2422
|
-
break;
|
|
2423
|
-
}
|
|
2424
|
-
if (chosen) break;
|
|
2425
|
-
}
|
|
2426
|
-
if (!chosen) return null;
|
|
2427
|
-
let counter = 1;
|
|
2428
|
-
for (let i = chosen.start; i < lines.length; i++) {
|
|
2429
|
-
if (chosen.re.start.exec(lines[i])?.[1] === regionName) {
|
|
2430
|
-
counter++;
|
|
2431
|
-
continue;
|
|
2432
|
-
}
|
|
2433
|
-
const endRegion = chosen.re.end.exec(lines[i])?.[1];
|
|
2434
|
-
if (endRegion === regionName || endRegion === "") {
|
|
2435
|
-
if (--counter === 0) return {
|
|
2436
|
-
...chosen,
|
|
2437
|
-
end: i
|
|
2438
|
-
};
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
return null;
|
|
2442
|
-
}
|
|
2443
|
-
const RE_SNIPPET_IMPORT = /^<<<[ \t]*(\S.*?)(#[\w-]+)?[ \t]*(?:[ \t](\S+?))?[ \t]*(\{.*)?$/;
|
|
2444
|
-
function MarkdownItSnippet(md, { userRoot, data: { watchFiles, slides } }) {
|
|
2445
|
-
md.block.ruler.before("fence", "snippet_import", (state, startLine, _endLine, silent) => {
|
|
2446
|
-
const pos = state.bMarks[startLine] + state.tShift[startLine];
|
|
2447
|
-
const max = state.eMarks[startLine];
|
|
2448
|
-
const match = state.src.slice(pos, max).match(RE_SNIPPET_IMPORT);
|
|
2449
|
-
if (!match) return false;
|
|
2450
|
-
if (silent) return true;
|
|
2451
|
-
let [, filepath = "", regionName = "", lang = "", meta = ""] = match;
|
|
2452
|
-
const slideNo = state.env.id?.match(regexSlideSourceId);
|
|
2453
|
-
const slide = slideNo ? slides[slideNo[1] - 1] : null;
|
|
2454
|
-
if (!slide) {
|
|
2455
|
-
console.warn(yellow(`[markdown-it-snippet] Snippet syntax is not supported in ${state.env.id || "unknown source"}. Skipped.`));
|
|
2456
|
-
return false;
|
|
2457
|
-
}
|
|
2458
|
-
const dir = path.dirname(slide.source.filepath);
|
|
2459
|
-
const src = slash(filepath.startsWith("@/") ? path.resolve(userRoot, filepath.slice(2)) : path.resolve(dir, filepath));
|
|
2460
|
-
lang = lang.trim() || path.extname(filepath).slice(1);
|
|
2461
|
-
meta = meta.trim();
|
|
2462
|
-
if (!(fs.existsSync(src) && fs.statSync(src).isFile())) throw new Error(`Code snippet path not found: ${src}`);
|
|
2463
|
-
let content = fs.readFileSync(src, "utf8");
|
|
2464
|
-
if (regionName) {
|
|
2465
|
-
const lines = content.split(RE_NEWLINE);
|
|
2466
|
-
const region = findRegion(lines, regionName.slice(1));
|
|
2467
|
-
if (region) content = dedent(lines.slice(region.start, region.end).filter((l) => !(region.re.start.test(l) || region.re.end.test(l))).join("\n"));
|
|
2468
|
-
}
|
|
2469
|
-
if (meta.includes("{monaco-write}")) {
|
|
2470
|
-
monacoWriterWhitelist.add(filepath);
|
|
2471
|
-
lang = lang.trim();
|
|
2472
|
-
meta = meta.replace("{monaco-write}", "").trim() || "{}";
|
|
2473
|
-
const safeFilepath = JSON.stringify(filepath).slice(1, -1);
|
|
2474
|
-
const encoded = lz.compressToBase64(content);
|
|
2475
|
-
const token = state.push("html_block", "", 0);
|
|
2476
|
-
token.content = `<Monaco writable="${safeFilepath}" code-lz="${encoded}" lang="${lang}" v-bind="${meta}" />\n`;
|
|
2477
|
-
token.map = [startLine, startLine + 1];
|
|
2478
|
-
} else {
|
|
2479
|
-
watchFiles[src] ??= /* @__PURE__ */ new Set();
|
|
2480
|
-
watchFiles[src].add(slide.index);
|
|
2481
|
-
const token = state.push("fence", "code", 0);
|
|
2482
|
-
token.info = `${lang} ${meta}`.trim();
|
|
2483
|
-
token.content = content.endsWith("\n") ? content : `${content}\n`;
|
|
2484
|
-
token.map = [startLine, startLine + 1];
|
|
2485
|
-
}
|
|
2486
|
-
state.line = startLine + 1;
|
|
2487
|
-
return true;
|
|
2488
|
-
}, { alt: [
|
|
2489
|
-
"paragraph",
|
|
2490
|
-
"reference",
|
|
2491
|
-
"blockquote",
|
|
2492
|
-
"list"
|
|
2493
|
-
] });
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
2513
|
//#endregion
|
|
2497
2514
|
//#region node/syntax/index.ts
|
|
2498
2515
|
async function useMarkdownItPlugins(md, options, markdownTransformMap, codeblockTransformers) {
|
|
@@ -2515,7 +2532,6 @@ async function useMarkdownItPlugins(md, options, markdownTransformMap, codeblock
|
|
|
2515
2532
|
md.use(MarkdownItStyleScoped);
|
|
2516
2533
|
md.use(MarkdownItGitHubAlerts);
|
|
2517
2534
|
}
|
|
2518
|
-
|
|
2519
2535
|
//#endregion
|
|
2520
2536
|
//#region node/vite/markdown.ts
|
|
2521
2537
|
const RE_MD_EXT$1 = /\.md$/;
|
|
@@ -2548,13 +2564,13 @@ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
|
|
|
2548
2564
|
},
|
|
2549
2565
|
transforms: {
|
|
2550
2566
|
...mdOptions?.transforms,
|
|
2551
|
-
async before(code, id
|
|
2552
|
-
if (options.data.markdownFiles[id
|
|
2553
|
-
code = await mdOptions?.transforms?.before?.(code, id
|
|
2554
|
-
const match = id
|
|
2567
|
+
async before(code, id) {
|
|
2568
|
+
if (options.data.markdownFiles[id]) return "";
|
|
2569
|
+
code = await mdOptions?.transforms?.before?.(code, id) ?? code;
|
|
2570
|
+
const match = id.match(regexSlideSourceId);
|
|
2555
2571
|
if (!match) return code;
|
|
2556
2572
|
const s = new MagicString(code);
|
|
2557
|
-
markdownTransformMap.set(id
|
|
2573
|
+
markdownTransformMap.set(id, s);
|
|
2558
2574
|
const ctx = {
|
|
2559
2575
|
s,
|
|
2560
2576
|
slide: options.data.slides[+match[1] - 1],
|
|
@@ -2570,19 +2586,503 @@ async function createMarkdownPlugin(options, { markdown: mdOptions }) {
|
|
|
2570
2586
|
}
|
|
2571
2587
|
});
|
|
2572
2588
|
}
|
|
2573
|
-
|
|
2589
|
+
//#endregion
|
|
2590
|
+
//#region node/mcp/operations.ts
|
|
2591
|
+
/**
|
|
2592
|
+
* Resolve a rendered slide (1-based, as displayed in the presentation) or
|
|
2593
|
+
* throw a descriptive error.
|
|
2594
|
+
*/
|
|
2595
|
+
function resolveSlide(data, no) {
|
|
2596
|
+
const slide = data.slides[no - 1];
|
|
2597
|
+
if (!slide) throw new Error(`Slide ${no} does not exist. The deck has ${data.slides.length} slides (1-${data.slides.length}).`);
|
|
2598
|
+
return slide;
|
|
2599
|
+
}
|
|
2600
|
+
function getMarkdown(data, source) {
|
|
2601
|
+
const md = data.markdownFiles[source.filepath];
|
|
2602
|
+
if (!md) throw new Error(`Markdown file not loaded: ${source.filepath}`);
|
|
2603
|
+
return md;
|
|
2604
|
+
}
|
|
2605
|
+
function assertNotEntryHeadmatter(data, source, action) {
|
|
2606
|
+
if (source.filepath === data.entry.filepath && data.entry.slides.indexOf(source) === 0) throw new Error(`Cannot ${action} the first slide of the entry file: its frontmatter is the deck headmatter (global configuration). Edit its content with the update tool instead, or operate on the following slides.`);
|
|
2607
|
+
}
|
|
2608
|
+
/**
|
|
2609
|
+
* Apply a `SlidePatch` to the slide source and save the markdown file.
|
|
2610
|
+
*
|
|
2611
|
+
* Note this only mutates the *source* slide (`slide.source`), not the
|
|
2612
|
+
* rendered `SlideInfo`, so a running dev server will pick up the change from
|
|
2613
|
+
* disk like an external edit and push HMR updates to connected clients.
|
|
2614
|
+
*/
|
|
2615
|
+
async function applySlidePatch(data, no, patch) {
|
|
2616
|
+
const slide = resolveSlide(data, no);
|
|
2617
|
+
const source = slide.source;
|
|
2618
|
+
if (patch.content != null) source.content = patch.content;
|
|
2619
|
+
if (patch.frontmatterRaw != null) if (patch.frontmatterRaw.trim() === "") source.frontmatterDoc = source.frontmatterStyle = void 0;
|
|
2620
|
+
else {
|
|
2621
|
+
const parsed = YAML.parseDocument(patch.frontmatterRaw);
|
|
2622
|
+
if (parsed.errors.length) throw new Error(`Invalid YAML frontmatter: ${parsed.errors.map((e) => e.message).join("; ")}`);
|
|
2623
|
+
source.frontmatterDoc = parsed;
|
|
2624
|
+
}
|
|
2625
|
+
if (patch.note != null) source.note = patch.note;
|
|
2626
|
+
if (patch.frontmatter) updateFrontmatterPatch(source, patch.frontmatter);
|
|
2627
|
+
parser.prettifySlide(source);
|
|
2628
|
+
return {
|
|
2629
|
+
slide,
|
|
2630
|
+
fileContent: await parser.save(getMarkdown(data, source))
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* Insert a new slide after an existing one, in the same markdown file.
|
|
2635
|
+
*/
|
|
2636
|
+
async function insertSlide(data, options) {
|
|
2637
|
+
const anchor = resolveSlide(data, options.after);
|
|
2638
|
+
const md = getMarkdown(data, anchor.source);
|
|
2639
|
+
const frontmatter = options.frontmatter ?? {};
|
|
2640
|
+
const doc = Object.keys(frontmatter).length > 0 ? new YAML.Document(frontmatter) : void 0;
|
|
2641
|
+
const source = {
|
|
2642
|
+
filepath: anchor.source.filepath,
|
|
2643
|
+
index: 0,
|
|
2644
|
+
start: 0,
|
|
2645
|
+
contentStart: 0,
|
|
2646
|
+
end: 0,
|
|
2647
|
+
raw: "",
|
|
2648
|
+
revision: "",
|
|
2649
|
+
content: options.content,
|
|
2650
|
+
contentRaw: options.content,
|
|
2651
|
+
frontmatter,
|
|
2652
|
+
frontmatterDoc: doc,
|
|
2653
|
+
frontmatterStyle: doc ? "frontmatter" : void 0,
|
|
2654
|
+
frontmatterRaw: doc?.toString(),
|
|
2655
|
+
note: options.note?.trim() || void 0
|
|
2656
|
+
};
|
|
2657
|
+
parser.prettifySlide(source);
|
|
2658
|
+
const anchorIdx = md.slides.indexOf(anchor.source);
|
|
2659
|
+
md.slides.splice(anchorIdx + 1, 0, source);
|
|
2660
|
+
const fileContent = await parser.save(md);
|
|
2661
|
+
return {
|
|
2662
|
+
source,
|
|
2663
|
+
filepath: md.filepath,
|
|
2664
|
+
fileContent
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
/**
|
|
2668
|
+
* Remove a slide from its source markdown file.
|
|
2669
|
+
*/
|
|
2670
|
+
async function removeSlide(data, no) {
|
|
2671
|
+
const slide = resolveSlide(data, no);
|
|
2672
|
+
assertNotEntryHeadmatter(data, slide.source, "remove");
|
|
2673
|
+
const md = getMarkdown(data, slide.source);
|
|
2674
|
+
const idx = md.slides.indexOf(slide.source);
|
|
2675
|
+
if (idx < 0) throw new Error(`Slide ${no} is out of sync with its source file. Try again.`);
|
|
2676
|
+
md.slides.splice(idx, 1);
|
|
2677
|
+
const fileContent = await parser.save(md);
|
|
2678
|
+
return {
|
|
2679
|
+
removed: slide,
|
|
2680
|
+
filepath: md.filepath,
|
|
2681
|
+
fileContent
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
/**
|
|
2685
|
+
* Move a slide before or after another slide within the same markdown file.
|
|
2686
|
+
*/
|
|
2687
|
+
async function moveSlide(data, options) {
|
|
2688
|
+
const { from, before, after } = options;
|
|
2689
|
+
if (before == null === (after == null)) throw new Error("Specify exactly one of `before` or `after`.");
|
|
2690
|
+
const anchorNo = before ?? after;
|
|
2691
|
+
if (anchorNo === from) throw new Error("The `before`/`after` anchor must be a different slide than `from`.");
|
|
2692
|
+
const slide = resolveSlide(data, from);
|
|
2693
|
+
const anchor = resolveSlide(data, anchorNo);
|
|
2694
|
+
if (slide.source.filepath !== anchor.source.filepath) throw new Error(`Cannot move a slide across markdown files: slide ${from} is in "${slide.source.filepath}" but slide ${anchorNo} is in "${anchor.source.filepath}" (imported with \`src:\`). Move it within its own file, or edit the \`src:\` imports in the entry file manually.`);
|
|
2695
|
+
assertNotEntryHeadmatter(data, slide.source, "move");
|
|
2696
|
+
if (before != null) assertNotEntryHeadmatter(data, anchor.source, "insert a slide before");
|
|
2697
|
+
const md = getMarkdown(data, slide.source);
|
|
2698
|
+
const fromIdx = md.slides.indexOf(slide.source);
|
|
2699
|
+
if (fromIdx < 0) throw new Error(`Slide ${from} is out of sync with its source file. Try again.`);
|
|
2700
|
+
md.slides.splice(fromIdx, 1);
|
|
2701
|
+
const anchorIdx = md.slides.indexOf(anchor.source);
|
|
2702
|
+
if (anchorIdx < 0) throw new Error(`Slide ${anchorNo} is out of sync with its source file. Try again.`);
|
|
2703
|
+
md.slides.splice(before != null ? anchorIdx : anchorIdx + 1, 0, slide.source);
|
|
2704
|
+
const fileContent = await parser.save(md);
|
|
2705
|
+
return {
|
|
2706
|
+
moved: slide,
|
|
2707
|
+
anchor,
|
|
2708
|
+
filepath: md.filepath,
|
|
2709
|
+
fileContent
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
//#endregion
|
|
2713
|
+
//#region node/mcp/server.ts
|
|
2714
|
+
function result(data) {
|
|
2715
|
+
return { content: [{
|
|
2716
|
+
type: "text",
|
|
2717
|
+
text: typeof data === "string" ? data : JSON.stringify(data, null, 2)
|
|
2718
|
+
}] };
|
|
2719
|
+
}
|
|
2720
|
+
function slideSummary(slide) {
|
|
2721
|
+
return {
|
|
2722
|
+
no: slide.index + 1,
|
|
2723
|
+
title: slide.title ?? null,
|
|
2724
|
+
...slide.frontmatter.layout ? { layout: slide.frontmatter.layout } : {},
|
|
2725
|
+
file: slide.source.filepath,
|
|
2726
|
+
hasNote: !!slide.source.note,
|
|
2727
|
+
...slide.importChain?.length ? { importedBySrcDirective: true } : {}
|
|
2728
|
+
};
|
|
2729
|
+
}
|
|
2730
|
+
const noSchema = z.number().int().min(1).describe("Slide number (1-based, as displayed in the presentation)");
|
|
2731
|
+
const frontmatterSchema = z.record(z.any()).optional().describe("Slide frontmatter (YAML headmatter of the slide) as an object, e.g. { \"layout\": \"two-cols\" }");
|
|
2732
|
+
/**
|
|
2733
|
+
* Create the Slidev MCP server exposing tools for agents to inspect, edit,
|
|
2734
|
+
* and (with a running dev server) navigate a slides deck.
|
|
2735
|
+
*
|
|
2736
|
+
* The same tool set is served over the dev server HTTP endpoint (`/__mcp`)
|
|
2737
|
+
* and the `slidev mcp` stdio command.
|
|
2738
|
+
*/
|
|
2739
|
+
function createSlidevMcpServer(ctx) {
|
|
2740
|
+
const server = new McpServer({
|
|
2741
|
+
name: "slidev",
|
|
2742
|
+
version: ctx.version
|
|
2743
|
+
}, { instructions: [
|
|
2744
|
+
"Tools for working with a Slidev (https://sli.dev) slides deck.",
|
|
2745
|
+
"A deck is a Markdown file where slides are separated by `---`; each slide can have YAML frontmatter, Markdown/Vue content, and a speaker note (trailing HTML comment).",
|
|
2746
|
+
"Slides are addressed by their rendered 1-based number, matching the slide numbers shown in the presentation.",
|
|
2747
|
+
"After editing tools run, a running dev server hot-reloads the presentation automatically."
|
|
2748
|
+
].join("\n") });
|
|
2749
|
+
server.registerTool("slidev-get-info", {
|
|
2750
|
+
title: "Get deck info",
|
|
2751
|
+
description: "Get an overview of the Slidev deck: entry file, title, slide count, markdown files, and (when a dev server is running) the server URL and current position of the live presentation.",
|
|
2752
|
+
annotations: { readOnlyHint: true }
|
|
2753
|
+
}, async () => {
|
|
2754
|
+
const data = await ctx.getData();
|
|
2755
|
+
const nav = ctx.nav?.getState();
|
|
2756
|
+
return result({
|
|
2757
|
+
slidevVersion: ctx.version,
|
|
2758
|
+
entry: ctx.entry,
|
|
2759
|
+
title: data.headmatter.title ?? data.slides[0]?.title ?? null,
|
|
2760
|
+
theme: data.config?.theme ?? data.headmatter.theme ?? null,
|
|
2761
|
+
totalSlides: data.slides.length,
|
|
2762
|
+
markdownFiles: Object.keys(data.markdownFiles),
|
|
2763
|
+
...ctx.getServerUrl?.() ? { server: {
|
|
2764
|
+
url: ctx.getServerUrl(),
|
|
2765
|
+
currentPage: nav?.page || null,
|
|
2766
|
+
currentClicks: nav?.page ? nav.clicks : null
|
|
2767
|
+
} } : {}
|
|
2768
|
+
});
|
|
2769
|
+
});
|
|
2770
|
+
server.registerTool("slidev-list-slides", {
|
|
2771
|
+
title: "List slides",
|
|
2772
|
+
description: "List all slides of the deck with their number, title, layout, and source file. Slides hidden with `hide`/`disabled` frontmatter are not included.",
|
|
2773
|
+
annotations: { readOnlyHint: true }
|
|
2774
|
+
}, async () => {
|
|
2775
|
+
return result((await ctx.getData()).slides.map(slideSummary));
|
|
2776
|
+
});
|
|
2777
|
+
server.registerTool("slidev-get-slide", {
|
|
2778
|
+
title: "Get slide",
|
|
2779
|
+
description: "Get the full source of one slide: frontmatter, Markdown content, and speaker note.",
|
|
2780
|
+
inputSchema: { no: noSchema },
|
|
2781
|
+
annotations: { readOnlyHint: true }
|
|
2782
|
+
}, async ({ no }) => {
|
|
2783
|
+
const slide = resolveSlide(await ctx.getData(), no);
|
|
2784
|
+
return result({
|
|
2785
|
+
...slideSummary(slide),
|
|
2786
|
+
frontmatter: slide.source.frontmatter,
|
|
2787
|
+
content: slide.source.content.trim(),
|
|
2788
|
+
note: slide.source.note ?? null,
|
|
2789
|
+
...slide.importChain?.length ? { importedBy: slide.importChain.map((s) => `${s.filepath}#${s.index + 1}`) } : {}
|
|
2790
|
+
});
|
|
2791
|
+
});
|
|
2792
|
+
server.registerTool("slidev-update-slide", {
|
|
2793
|
+
title: "Update slide",
|
|
2794
|
+
description: "Update the content, speaker note, and/or frontmatter of a slide. Only the provided fields are changed. Pass an empty string to clear the content or note. In `frontmatter`, only the given keys are patched; pass `null` as a value to delete that key.",
|
|
2795
|
+
inputSchema: {
|
|
2796
|
+
no: noSchema,
|
|
2797
|
+
content: z.string().optional().describe("New Markdown content of the slide (without frontmatter and note)"),
|
|
2798
|
+
note: z.string().optional().describe("New speaker note (Markdown, stored as a trailing HTML comment)"),
|
|
2799
|
+
frontmatter: frontmatterSchema
|
|
2800
|
+
}
|
|
2801
|
+
}, async ({ no, content, note, frontmatter }) => {
|
|
2802
|
+
if (content == null && note == null && frontmatter == null) throw new Error("Nothing to update: provide at least one of `content`, `note`, or `frontmatter`.");
|
|
2803
|
+
const { slide } = await applySlidePatch(await ctx.getData(), no, {
|
|
2804
|
+
content,
|
|
2805
|
+
note,
|
|
2806
|
+
frontmatter
|
|
2807
|
+
});
|
|
2808
|
+
return result(`Updated slide ${no} in ${slide.source.filepath}.`);
|
|
2809
|
+
});
|
|
2810
|
+
server.registerTool("slidev-insert-slide", {
|
|
2811
|
+
title: "Insert slide",
|
|
2812
|
+
description: "Insert a new slide after an existing slide (into the same markdown file). To add a slide at the very end, pass the last slide number.",
|
|
2813
|
+
inputSchema: {
|
|
2814
|
+
after: z.number().int().min(1).describe("Slide number (1-based) after which the new slide is inserted"),
|
|
2815
|
+
content: z.string().describe("Markdown content of the new slide"),
|
|
2816
|
+
frontmatter: frontmatterSchema,
|
|
2817
|
+
note: z.string().optional().describe("Speaker note of the new slide")
|
|
2818
|
+
}
|
|
2819
|
+
}, async ({ after, content, frontmatter, note }) => {
|
|
2820
|
+
const { filepath } = await insertSlide(await ctx.getData(), {
|
|
2821
|
+
after,
|
|
2822
|
+
content,
|
|
2823
|
+
frontmatter,
|
|
2824
|
+
note
|
|
2825
|
+
});
|
|
2826
|
+
return result(`Inserted a new slide after slide ${after} in ${filepath}. Slide numbers after it have shifted; list the slides again if needed.`);
|
|
2827
|
+
});
|
|
2828
|
+
server.registerTool("slidev-remove-slide", {
|
|
2829
|
+
title: "Remove slide",
|
|
2830
|
+
description: "Remove a slide from the deck (deletes it from its source markdown file).",
|
|
2831
|
+
inputSchema: { no: noSchema },
|
|
2832
|
+
annotations: { destructiveHint: true }
|
|
2833
|
+
}, async ({ no }) => {
|
|
2834
|
+
const { removed, filepath } = await removeSlide(await ctx.getData(), no);
|
|
2835
|
+
return result(`Removed slide ${no}${removed.title ? ` ("${removed.title}")` : ""} from ${filepath}. Slide numbers after it have shifted; list the slides again if needed.`);
|
|
2836
|
+
});
|
|
2837
|
+
server.registerTool("slidev-move-slide", {
|
|
2838
|
+
title: "Move slide",
|
|
2839
|
+
description: "Move a slide before or after another slide to reorder the deck. Both slides must be in the same markdown file. To swap two adjacent slides, move one after the other.",
|
|
2840
|
+
inputSchema: {
|
|
2841
|
+
from: z.number().int().min(1).describe("Slide number (1-based) of the slide to move"),
|
|
2842
|
+
before: z.number().int().min(1).optional().describe("Move the slide right before this slide number"),
|
|
2843
|
+
after: z.number().int().min(1).optional().describe("Move the slide right after this slide number")
|
|
2844
|
+
}
|
|
2845
|
+
}, async ({ from, before, after }) => {
|
|
2846
|
+
const { filepath } = await moveSlide(await ctx.getData(), {
|
|
2847
|
+
from,
|
|
2848
|
+
before,
|
|
2849
|
+
after
|
|
2850
|
+
});
|
|
2851
|
+
return result(`Moved slide ${from} ${before != null ? `before slide ${before}` : `after slide ${after}`} in ${filepath}. Slide numbers have shifted; list the slides again if needed.`);
|
|
2852
|
+
});
|
|
2853
|
+
if (ctx.nav) {
|
|
2854
|
+
const nav = ctx.nav;
|
|
2855
|
+
server.registerTool("slidev-goto-slide", {
|
|
2856
|
+
title: "Go to slide",
|
|
2857
|
+
description: "Navigate the live presentation (all connected browsers) to a given slide, e.g. to visually verify a slide after editing it.",
|
|
2858
|
+
inputSchema: {
|
|
2859
|
+
no: noSchema,
|
|
2860
|
+
clicks: z.number().int().min(0).optional().describe("Click animation step to reveal (defaults to 0)")
|
|
2861
|
+
},
|
|
2862
|
+
annotations: { idempotentHint: true }
|
|
2863
|
+
}, async ({ no, clicks }) => {
|
|
2864
|
+
resolveSlide(await ctx.getData(), no);
|
|
2865
|
+
nav.go(no, clicks ?? 0);
|
|
2866
|
+
return result(`Navigated the presentation to slide ${no}${clicks ? ` (click ${clicks})` : ""}.`);
|
|
2867
|
+
});
|
|
2868
|
+
}
|
|
2869
|
+
return server;
|
|
2870
|
+
}
|
|
2871
|
+
//#endregion
|
|
2872
|
+
//#region node/integrations/drawings.ts
|
|
2873
|
+
function resolveDrawingsDir(options) {
|
|
2874
|
+
return options.data.config.drawings.persist ? resolve(dirname(options.entry), options.data.config.drawings.persist) : void 0;
|
|
2875
|
+
}
|
|
2876
|
+
async function loadDrawings(options) {
|
|
2877
|
+
const dir = resolveDrawingsDir(options);
|
|
2878
|
+
if (!dir || !existsSync(dir)) return {};
|
|
2879
|
+
const files = await fg("*.svg", {
|
|
2880
|
+
onlyFiles: true,
|
|
2881
|
+
cwd: dir,
|
|
2882
|
+
absolute: true,
|
|
2883
|
+
suppressErrors: true
|
|
2884
|
+
});
|
|
2885
|
+
const obj = {};
|
|
2886
|
+
await Promise.all(files.map(async (path) => {
|
|
2887
|
+
const num = +basename(path, ".svg");
|
|
2888
|
+
if (Number.isNaN(num)) return;
|
|
2889
|
+
const lines = (await fs$1.readFile(path, "utf8")).split(/\n/g);
|
|
2890
|
+
obj[num.toString()] = lines.slice(1, -1).join("\n");
|
|
2891
|
+
}));
|
|
2892
|
+
return obj;
|
|
2893
|
+
}
|
|
2894
|
+
async function writeDrawings(options, drawing) {
|
|
2895
|
+
const dir = resolveDrawingsDir(options);
|
|
2896
|
+
if (!dir) return;
|
|
2897
|
+
const width = options.data.config.canvasWidth;
|
|
2898
|
+
const SVG_HEAD = `<svg width="${width}" height="${Math.round(width / options.data.config.aspectRatio)}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`;
|
|
2899
|
+
await fs$1.mkdir(dir, { recursive: true });
|
|
2900
|
+
return Promise.all(Object.entries(drawing).map(async ([key, value]) => {
|
|
2901
|
+
if (!value) return;
|
|
2902
|
+
const svg = `${SVG_HEAD}\n${value}\n</svg>`;
|
|
2903
|
+
await fs$1.writeFile(join(dir, `${key}.svg`), svg, "utf-8");
|
|
2904
|
+
}));
|
|
2905
|
+
}
|
|
2906
|
+
//#endregion
|
|
2907
|
+
//#region node/integrations/snapshots.ts
|
|
2908
|
+
function resolveSnapshotsDir(options) {
|
|
2909
|
+
return resolve(dirname(options.entry), ".slidev/snapshots");
|
|
2910
|
+
}
|
|
2911
|
+
async function loadSnapshots(options) {
|
|
2912
|
+
const dir = resolveSnapshotsDir(options);
|
|
2913
|
+
const file = join(dir, "snapshots.json");
|
|
2914
|
+
if (!dir || !existsSync(file)) return {};
|
|
2915
|
+
return JSON.parse(await fs$1.readFile(file, "utf8"));
|
|
2916
|
+
}
|
|
2917
|
+
async function writeSnapshots(options, data) {
|
|
2918
|
+
const dir = resolveSnapshotsDir(options);
|
|
2919
|
+
if (!dir) return;
|
|
2920
|
+
await fs$1.mkdir(dir, { recursive: true });
|
|
2921
|
+
await fs$1.writeFile(join(dir, "snapshots.json"), JSON.stringify(data, null, 2), "utf-8");
|
|
2922
|
+
}
|
|
2923
|
+
//#endregion
|
|
2924
|
+
//#region node/vite/serverRef.ts
|
|
2925
|
+
const serverRefStates = /* @__PURE__ */ new WeakMap();
|
|
2926
|
+
/**
|
|
2927
|
+
* Access the server-side state shared with the clients via
|
|
2928
|
+
* `vite-plugin-vue-server-ref` (e.g. `nav` holds the current position of the
|
|
2929
|
+
* live presentation).
|
|
2930
|
+
*/
|
|
2931
|
+
function getServerRefState(options) {
|
|
2932
|
+
return serverRefStates.get(options);
|
|
2933
|
+
}
|
|
2934
|
+
async function createServerRefPlugin(options, pluginOptions) {
|
|
2935
|
+
const state = {
|
|
2936
|
+
sync: false,
|
|
2937
|
+
nav: {
|
|
2938
|
+
page: 0,
|
|
2939
|
+
clicks: 0,
|
|
2940
|
+
timer: {
|
|
2941
|
+
status: "stopped",
|
|
2942
|
+
slides: {},
|
|
2943
|
+
startedAt: 0,
|
|
2944
|
+
pausedAt: 0
|
|
2945
|
+
}
|
|
2946
|
+
},
|
|
2947
|
+
drawings: await loadDrawings(options),
|
|
2948
|
+
snapshots: await loadSnapshots(options),
|
|
2949
|
+
...pluginOptions.serverRef?.state
|
|
2950
|
+
};
|
|
2951
|
+
serverRefStates.set(options, state);
|
|
2952
|
+
return ServerRef({
|
|
2953
|
+
debug: false,
|
|
2954
|
+
state,
|
|
2955
|
+
onChanged(key, data, patch, timestamp) {
|
|
2956
|
+
pluginOptions.serverRef?.onChanged?.(key, data, patch, timestamp);
|
|
2957
|
+
if (options.data.config.drawings.persist && key === "drawings") writeDrawings(options, patch ?? data);
|
|
2958
|
+
if (key === "snapshots") writeSnapshots(options, data);
|
|
2959
|
+
}
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
const SERVER_REF_WS_EVENT = "vue-server-ref";
|
|
2963
|
+
const SERVER_REF_NAV_MODULE_IDS = ["/@server-reactive/nav", "/@server-ref/nav"];
|
|
2964
|
+
/**
|
|
2965
|
+
* Serves the Slidev MCP (Model Context Protocol) server on the dev server at
|
|
2966
|
+
* `/__mcp` (streamable HTTP transport), so AI agents can inspect, edit, and
|
|
2967
|
+
* navigate the slides. Can be disabled with `mcp: false` in the headmatter.
|
|
2968
|
+
*/
|
|
2969
|
+
function createMcpPlugin(options) {
|
|
2970
|
+
return {
|
|
2971
|
+
name: "slidev:mcp",
|
|
2972
|
+
apply: "serve",
|
|
2973
|
+
configureServer(server) {
|
|
2974
|
+
if (options.mode !== "dev") return;
|
|
2975
|
+
const ctx = {
|
|
2976
|
+
version,
|
|
2977
|
+
entry: options.entry,
|
|
2978
|
+
getData: () => options.data,
|
|
2979
|
+
getServerUrl: () => server.resolvedUrls?.local[0],
|
|
2980
|
+
nav: {
|
|
2981
|
+
getState: () => {
|
|
2982
|
+
const nav = getServerRefState(options)?.nav;
|
|
2983
|
+
return {
|
|
2984
|
+
page: nav?.page ?? 0,
|
|
2985
|
+
clicks: nav?.clicks ?? 0
|
|
2986
|
+
};
|
|
2987
|
+
},
|
|
2988
|
+
go: (page, clicks) => navigateClients(server, options, page, clicks)
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
server.middlewares.use(async (req, res, next) => {
|
|
2992
|
+
if (req.url?.split("?")[0]?.replace(/\/$/, "") !== "/__mcp") return next();
|
|
2993
|
+
if (options.data.config.mcp === false) {
|
|
2994
|
+
res.statusCode = 403;
|
|
2995
|
+
res.setHeader("Content-Type", "application/json");
|
|
2996
|
+
res.end(JSON.stringify({
|
|
2997
|
+
jsonrpc: "2.0",
|
|
2998
|
+
error: {
|
|
2999
|
+
code: -32e3,
|
|
3000
|
+
message: "The Slidev MCP server is disabled (`mcp: false` in the headmatter)"
|
|
3001
|
+
},
|
|
3002
|
+
id: null
|
|
3003
|
+
}));
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
try {
|
|
3007
|
+
const mcp = createSlidevMcpServer(ctx);
|
|
3008
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
3009
|
+
res.on("close", () => {
|
|
3010
|
+
transport.close();
|
|
3011
|
+
mcp.close();
|
|
3012
|
+
});
|
|
3013
|
+
await mcp.connect(transport);
|
|
3014
|
+
await transport.handleRequest(req, res);
|
|
3015
|
+
} catch (e) {
|
|
3016
|
+
console.error("[slidev] Error handling MCP request:", e);
|
|
3017
|
+
if (!res.headersSent) {
|
|
3018
|
+
res.statusCode = 500;
|
|
3019
|
+
res.setHeader("Content-Type", "application/json");
|
|
3020
|
+
res.end(JSON.stringify({
|
|
3021
|
+
jsonrpc: "2.0",
|
|
3022
|
+
error: {
|
|
3023
|
+
code: -32603,
|
|
3024
|
+
message: "Internal server error"
|
|
3025
|
+
},
|
|
3026
|
+
id: null
|
|
3027
|
+
}));
|
|
3028
|
+
}
|
|
3029
|
+
}
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
/**
|
|
3035
|
+
* Drive all connected clients to the given slide by patching the shared
|
|
3036
|
+
* `nav` state, mimicking what `vite-plugin-vue-server-ref` does when a
|
|
3037
|
+
* presenter or remote-control client navigates.
|
|
3038
|
+
*/
|
|
3039
|
+
function navigateClients(server, options, page, clicks) {
|
|
3040
|
+
const time = Date.now();
|
|
3041
|
+
const nav = getServerRefState(options)?.nav;
|
|
3042
|
+
if (nav) {
|
|
3043
|
+
nav.page = page;
|
|
3044
|
+
nav.clicks = clicks;
|
|
3045
|
+
nav.lastUpdate = {
|
|
3046
|
+
id: "slidev-mcp",
|
|
3047
|
+
type: "presenter",
|
|
3048
|
+
time
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
for (const id of SERVER_REF_NAV_MODULE_IDS) {
|
|
3052
|
+
const module = server.moduleGraph.getModuleById(id);
|
|
3053
|
+
if (module) server.moduleGraph.invalidateModule(module);
|
|
3054
|
+
}
|
|
3055
|
+
for (const type of ["presenter", "viewer"]) server.hot.send({
|
|
3056
|
+
type: "custom",
|
|
3057
|
+
event: SERVER_REF_WS_EVENT,
|
|
3058
|
+
data: {
|
|
3059
|
+
key: "nav",
|
|
3060
|
+
source: "slidev-mcp",
|
|
3061
|
+
patch: {
|
|
3062
|
+
page,
|
|
3063
|
+
clicks,
|
|
3064
|
+
lastUpdate: {
|
|
3065
|
+
id: "slidev-mcp",
|
|
3066
|
+
type,
|
|
3067
|
+
time
|
|
3068
|
+
}
|
|
3069
|
+
},
|
|
3070
|
+
timestamp: time
|
|
3071
|
+
}
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
2574
3074
|
//#endregion
|
|
2575
3075
|
//#region node/vite/monacoTypes.ts
|
|
2576
3076
|
function createMonacoTypesLoader({ userRoot, utils }) {
|
|
2577
3077
|
return {
|
|
2578
3078
|
name: "slidev:monaco-types-loader",
|
|
2579
|
-
resolveId(id
|
|
2580
|
-
if (id
|
|
3079
|
+
resolveId(id) {
|
|
3080
|
+
if (id.startsWith("/@slidev-monaco-types/")) return id;
|
|
2581
3081
|
return null;
|
|
2582
3082
|
},
|
|
2583
|
-
async load(id
|
|
2584
|
-
if (!id
|
|
2585
|
-
const url = new URL(id
|
|
3083
|
+
async load(id) {
|
|
3084
|
+
if (!id.startsWith("/@slidev-monaco-types/")) return null;
|
|
3085
|
+
const url = new URL(id, "http://localhost");
|
|
2586
3086
|
if (url.pathname === "/@slidev-monaco-types/resolve") {
|
|
2587
3087
|
const query = new URLSearchParams(url.search);
|
|
2588
3088
|
const pkg = query.get("pkg");
|
|
@@ -2592,7 +3092,7 @@ function createMonacoTypesLoader({ userRoot, utils }) {
|
|
|
2592
3092
|
const root = slash(dirname(pkgJsonPath));
|
|
2593
3093
|
const pkgJson = JSON.parse(await fs$1.readFile(pkgJsonPath, "utf-8"));
|
|
2594
3094
|
let deps = Object.keys(pkgJson.dependencies ?? {});
|
|
2595
|
-
deps = deps.filter((pkg
|
|
3095
|
+
deps = deps.filter((pkg) => !utils.isMonacoTypesIgnored(pkg));
|
|
2596
3096
|
return [`import "/@slidev-monaco-types/load?${new URLSearchParams({
|
|
2597
3097
|
root,
|
|
2598
3098
|
name: pkgJson.name
|
|
@@ -2621,7 +3121,6 @@ function createMonacoTypesLoader({ userRoot, utils }) {
|
|
|
2621
3121
|
}
|
|
2622
3122
|
};
|
|
2623
3123
|
}
|
|
2624
|
-
|
|
2625
3124
|
//#endregion
|
|
2626
3125
|
//#region node/vite/patchMonacoSourceMap.ts
|
|
2627
3126
|
const postfixRE = /[?#].*$/;
|
|
@@ -2635,20 +3134,47 @@ function createPatchMonacoSourceMapPlugin() {
|
|
|
2635
3134
|
return {
|
|
2636
3135
|
name: "slidev:patch-monaco-sourcemap",
|
|
2637
3136
|
enforce: "pre",
|
|
2638
|
-
load(id
|
|
2639
|
-
if (!id
|
|
2640
|
-
return readFile(cleanUrl(id
|
|
3137
|
+
load(id) {
|
|
3138
|
+
if (!id.includes("node_modules/monaco-editor/esm/vs/base")) return;
|
|
3139
|
+
return readFile(cleanUrl(id), "utf-8");
|
|
2641
3140
|
}
|
|
2642
3141
|
};
|
|
2643
3142
|
}
|
|
2644
|
-
|
|
3143
|
+
//#endregion
|
|
3144
|
+
//#region node/vite/pwa.ts
|
|
3145
|
+
function createPWAPlugin(options) {
|
|
3146
|
+
const matchMode = (mode) => mode === true || mode === options.mode;
|
|
3147
|
+
const enabled = matchMode(options.data.config.pwa);
|
|
3148
|
+
const base = options.base ?? "/";
|
|
3149
|
+
const title = options.data.config.title || "Slidev";
|
|
3150
|
+
return VitePWA({
|
|
3151
|
+
disable: !enabled,
|
|
3152
|
+
registerType: "autoUpdate",
|
|
3153
|
+
injectRegister: null,
|
|
3154
|
+
base,
|
|
3155
|
+
workbox: {
|
|
3156
|
+
maximumFileSizeToCacheInBytes: 100 * 1024 * 1024,
|
|
3157
|
+
globPatterns: ["**/*.{js,css,html,woff,woff2,ttf,png,jpg,jpeg,svg,gif,webp,avif,ico,mp4,webm,ogv,mov,m4v,mp3,wav,ogg,oga,m4a,aac,flac,opus,weba}"],
|
|
3158
|
+
navigateFallback: "index.html",
|
|
3159
|
+
navigateFallbackDenylist: [/^\/(presenter|notes|overview|print|export)/]
|
|
3160
|
+
},
|
|
3161
|
+
manifest: {
|
|
3162
|
+
name: title,
|
|
3163
|
+
short_name: "Slidev",
|
|
3164
|
+
start_url: base,
|
|
3165
|
+
display: "fullscreen",
|
|
3166
|
+
theme_color: "#121212",
|
|
3167
|
+
background_color: "#121212"
|
|
3168
|
+
}
|
|
3169
|
+
});
|
|
3170
|
+
}
|
|
2645
3171
|
//#endregion
|
|
2646
3172
|
//#region node/vite/remoteAssets.ts
|
|
2647
3173
|
async function createRemoteAssetsPlugin({ data: { config }, mode }, pluginOptions) {
|
|
2648
3174
|
if (!(config.remoteAssets === true || config.remoteAssets === mode)) return;
|
|
2649
3175
|
const { VitePluginRemoteAssets, DefaultRules } = await import("vite-plugin-remote-assets");
|
|
2650
3176
|
return VitePluginRemoteAssets({
|
|
2651
|
-
resolveMode: (id
|
|
3177
|
+
resolveMode: (id) => id.endsWith("index.html") ? "relative" : "@fs",
|
|
2652
3178
|
awaitDownload: mode === "build",
|
|
2653
3179
|
...pluginOptions.remoteAssets,
|
|
2654
3180
|
rules: [
|
|
@@ -2661,90 +3187,6 @@ async function createRemoteAssetsPlugin({ data: { config }, mode }, pluginOption
|
|
|
2661
3187
|
]
|
|
2662
3188
|
});
|
|
2663
3189
|
}
|
|
2664
|
-
|
|
2665
|
-
//#endregion
|
|
2666
|
-
//#region node/integrations/drawings.ts
|
|
2667
|
-
function resolveDrawingsDir(options) {
|
|
2668
|
-
return options.data.config.drawings.persist ? resolve(dirname(options.entry), options.data.config.drawings.persist) : void 0;
|
|
2669
|
-
}
|
|
2670
|
-
async function loadDrawings(options) {
|
|
2671
|
-
const dir = resolveDrawingsDir(options);
|
|
2672
|
-
if (!dir || !existsSync(dir)) return {};
|
|
2673
|
-
const files = await fg("*.svg", {
|
|
2674
|
-
onlyFiles: true,
|
|
2675
|
-
cwd: dir,
|
|
2676
|
-
absolute: true,
|
|
2677
|
-
suppressErrors: true
|
|
2678
|
-
});
|
|
2679
|
-
const obj = {};
|
|
2680
|
-
await Promise.all(files.map(async (path$1) => {
|
|
2681
|
-
const num = +basename(path$1, ".svg");
|
|
2682
|
-
if (Number.isNaN(num)) return;
|
|
2683
|
-
const lines = (await fs$1.readFile(path$1, "utf8")).split(/\n/g);
|
|
2684
|
-
obj[num.toString()] = lines.slice(1, -1).join("\n");
|
|
2685
|
-
}));
|
|
2686
|
-
return obj;
|
|
2687
|
-
}
|
|
2688
|
-
async function writeDrawings(options, drawing) {
|
|
2689
|
-
const dir = resolveDrawingsDir(options);
|
|
2690
|
-
if (!dir) return;
|
|
2691
|
-
const width = options.data.config.canvasWidth;
|
|
2692
|
-
const SVG_HEAD = `<svg width="${width}" height="${Math.round(width / options.data.config.aspectRatio)}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">`;
|
|
2693
|
-
await fs$1.mkdir(dir, { recursive: true });
|
|
2694
|
-
return Promise.all(Object.entries(drawing).map(async ([key, value]) => {
|
|
2695
|
-
if (!value) return;
|
|
2696
|
-
const svg = `${SVG_HEAD}\n${value}\n</svg>`;
|
|
2697
|
-
await fs$1.writeFile(join(dir, `${key}.svg`), svg, "utf-8");
|
|
2698
|
-
}));
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
//#endregion
|
|
2702
|
-
//#region node/integrations/snapshots.ts
|
|
2703
|
-
function resolveSnapshotsDir(options) {
|
|
2704
|
-
return resolve(dirname(options.entry), ".slidev/snapshots");
|
|
2705
|
-
}
|
|
2706
|
-
async function loadSnapshots(options) {
|
|
2707
|
-
const dir = resolveSnapshotsDir(options);
|
|
2708
|
-
const file = join(dir, "snapshots.json");
|
|
2709
|
-
if (!dir || !existsSync(file)) return {};
|
|
2710
|
-
return JSON.parse(await fs$1.readFile(file, "utf8"));
|
|
2711
|
-
}
|
|
2712
|
-
async function writeSnapshots(options, data) {
|
|
2713
|
-
const dir = resolveSnapshotsDir(options);
|
|
2714
|
-
if (!dir) return;
|
|
2715
|
-
await fs$1.mkdir(dir, { recursive: true });
|
|
2716
|
-
await fs$1.writeFile(join(dir, "snapshots.json"), JSON.stringify(data, null, 2), "utf-8");
|
|
2717
|
-
}
|
|
2718
|
-
|
|
2719
|
-
//#endregion
|
|
2720
|
-
//#region node/vite/serverRef.ts
|
|
2721
|
-
async function createServerRefPlugin(options, pluginOptions) {
|
|
2722
|
-
return ServerRef({
|
|
2723
|
-
debug: false,
|
|
2724
|
-
state: {
|
|
2725
|
-
sync: false,
|
|
2726
|
-
nav: {
|
|
2727
|
-
page: 0,
|
|
2728
|
-
clicks: 0,
|
|
2729
|
-
timer: {
|
|
2730
|
-
status: "stopped",
|
|
2731
|
-
slides: {},
|
|
2732
|
-
startedAt: 0,
|
|
2733
|
-
pausedAt: 0
|
|
2734
|
-
}
|
|
2735
|
-
},
|
|
2736
|
-
drawings: await loadDrawings(options),
|
|
2737
|
-
snapshots: await loadSnapshots(options),
|
|
2738
|
-
...pluginOptions.serverRef?.state
|
|
2739
|
-
},
|
|
2740
|
-
onChanged(key, data, patch, timestamp) {
|
|
2741
|
-
pluginOptions.serverRef?.onChanged?.(key, data, patch, timestamp);
|
|
2742
|
-
if (options.data.config.drawings.persist && key === "drawings") writeDrawings(options, patch ?? data);
|
|
2743
|
-
if (key === "snapshots") writeSnapshots(options, data);
|
|
2744
|
-
}
|
|
2745
|
-
});
|
|
2746
|
-
}
|
|
2747
|
-
|
|
2748
3190
|
//#endregion
|
|
2749
3191
|
//#region node/vite/staticCopy.ts
|
|
2750
3192
|
async function createStaticCopyPlugin({ themeRoots, addonRoots }, pluginOptions) {
|
|
@@ -2760,7 +3202,6 @@ async function createStaticCopyPlugin({ themeRoots, addonRoots }, pluginOptions)
|
|
|
2760
3202
|
...pluginOptions.staticCopy
|
|
2761
3203
|
});
|
|
2762
3204
|
}
|
|
2763
|
-
|
|
2764
3205
|
//#endregion
|
|
2765
3206
|
//#region node/setups/unocss.ts
|
|
2766
3207
|
async function setupUnocss({ clientRoot, roots, data, utils }) {
|
|
@@ -2789,7 +3230,6 @@ async function setupUnocss({ clientRoot, roots, data, utils }) {
|
|
|
2789
3230
|
config.theme.fontFamily.serif ||= data.config.fonts.serif.join(",");
|
|
2790
3231
|
return config;
|
|
2791
3232
|
}
|
|
2792
|
-
|
|
2793
3233
|
//#endregion
|
|
2794
3234
|
//#region node/vite/unocss.ts
|
|
2795
3235
|
async function createUnocssPlugin(options, pluginOptions) {
|
|
@@ -2799,7 +3239,6 @@ async function createUnocssPlugin(options, pluginOptions) {
|
|
|
2799
3239
|
...pluginOptions.unocss
|
|
2800
3240
|
});
|
|
2801
3241
|
}
|
|
2802
|
-
|
|
2803
3242
|
//#endregion
|
|
2804
3243
|
//#region node/vite/vue.ts
|
|
2805
3244
|
const RE_VUE_EXT = /\.vue$/;
|
|
@@ -2807,7 +3246,7 @@ const RE_VUE_QUERY_VUE = /\.vue\?vue/;
|
|
|
2807
3246
|
const RE_VUE_QUERY_V = /\.vue\?v=/;
|
|
2808
3247
|
const RE_MD_EXT = /\.md$/;
|
|
2809
3248
|
const RE_MD_QUERY_VUE = /\.md\?vue/;
|
|
2810
|
-
const customElements = new Set([
|
|
3249
|
+
const customElements = /* @__PURE__ */ new Set([
|
|
2811
3250
|
"annotation",
|
|
2812
3251
|
"math",
|
|
2813
3252
|
"menclose",
|
|
@@ -2860,12 +3299,12 @@ async function createVuePlugin(_options, pluginOptions) {
|
|
|
2860
3299
|
});
|
|
2861
3300
|
return [VueJsx(vuejsxOptions), VuePlugin];
|
|
2862
3301
|
}
|
|
2863
|
-
|
|
2864
3302
|
//#endregion
|
|
2865
3303
|
//#region node/vite/index.ts
|
|
2866
3304
|
function ViteSlidevPlugin(options, pluginOptions = {}, serverOptions = {}) {
|
|
2867
3305
|
return Promise.all([
|
|
2868
3306
|
createSlidesLoader(options, serverOptions),
|
|
3307
|
+
createMcpPlugin(options),
|
|
2869
3308
|
createMarkdownPlugin(options, pluginOptions),
|
|
2870
3309
|
createLayoutWrapperPlugin(options),
|
|
2871
3310
|
createContextInjectionPlugin(),
|
|
@@ -2884,10 +3323,10 @@ function ViteSlidevPlugin(options, pluginOptions = {}, serverOptions = {}) {
|
|
|
2884
3323
|
createStaticCopyPlugin(options, pluginOptions),
|
|
2885
3324
|
createInspectPlugin(options, pluginOptions),
|
|
2886
3325
|
createPatchMonacoSourceMapPlugin(),
|
|
3326
|
+
createPWAPlugin(options),
|
|
2887
3327
|
setupVitePlugins(options)
|
|
2888
3328
|
]);
|
|
2889
3329
|
}
|
|
2890
|
-
|
|
2891
3330
|
//#endregion
|
|
2892
3331
|
//#region node/commands/shared.ts
|
|
2893
3332
|
const sharedMd = MarkdownExit({ html: true });
|
|
@@ -2917,6 +3356,11 @@ async function resolveViteConfigs(options, baseConfig, overrideConfigs, command,
|
|
|
2917
3356
|
});
|
|
2918
3357
|
return baseConfig;
|
|
2919
3358
|
}
|
|
2920
|
-
|
|
2921
3359
|
//#endregion
|
|
2922
|
-
|
|
3360
|
+
//#region node/commands/serve.ts
|
|
3361
|
+
async function createServer$1(options, viteConfig = {}, serverOptions) {
|
|
3362
|
+
process.env.EDITOR = process.env.EDITOR || "code";
|
|
3363
|
+
return await createServer(await resolveViteConfigs(options, { optimizeDeps: { entries: [join(options.clientRoot, "main.ts")] } }, viteConfig, "serve", serverOptions));
|
|
3364
|
+
}
|
|
3365
|
+
//#endregion
|
|
3366
|
+
export { createDataUtils as a, getThemeMeta as c, loadSetups as d, updateFrontmatterPatch as f, createSlidevMcpServer as i, resolveTheme as l, resolveViteConfigs as n, resolveOptions as o, ViteSlidevPlugin as r, parser as s, createServer$1 as t, version as u };
|