@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.
@@ -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-BhqywfGz.mjs";
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 { loadConfigFromFile, mergeConfig, parseSync } from "vite";
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$1) {
108
- if (path$1.length === 1 && YAML.isScalar(node.key) && node.key.value === key) {
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$2, reject) => {
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$2(JSON.parse(body) || {});
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 makeAbsoluteImportGlob(self, globs, options = {}, baseRoot) {
141
- const root = baseRoot && globs.some((glob) => RE_WINDOWS_DRIVE.test(slash(glob))) ? baseRoot : void 0;
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(root ?? self, glob);
144
- return root && !relativeGlob.startsWith(".") && !RE_WINDOWS_DRIVE.test(relativeGlob) ? `./${relativeGlob}` : relativeGlob;
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$1 = resolve(root, "setup", filename);
161
- if (existsSync(path$1)) tasks.push(loadModule(path$1).then((mod) => mod.default(...args)));
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$2) {
185
- if (!RE_VUE_FILE.test(id$2) && !id$2.includes("?vue&")) return;
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$2) {
251
- if (!id$2.endsWith(".vue") || id$2.includes("/@slidev/client/") || id$2.includes("/packages/client/")) return;
252
- if (code.includes(templateInjectionMarker) || code.includes("useSlideContext()")) return code;
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$2) {
416
- if (id$2.startsWith("/@slidev-monaco-types/") || id$2.includes("/@slidev/monaco-types") || id$2.endsWith("?monaco-types&raw")) return "monaco/bundled-types";
417
- if (id$2.includes("/shiki/") || id$2.includes("/@shikijs/")) return `modules/shiki`;
418
- if (id$2.startsWith("~icons/")) return "modules/unplugin-icons";
419
- const matchedAsyncModule = ASYNC_MODULES.find((i) => id$2.includes(`/node_modules/${i}`));
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$2) {
426
- return id$2.includes("/@slidev/") || id$2.includes("/slidev/packages/client/") || id$2.includes("/@vueuse/");
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$2) {
459
- if (!id$2.match(regexSlideSourceId)) return;
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$2) {
539
- if (!isSlideMarkdownId(id$2) || !config?.server.fs.strict) return null;
540
- const importer = filePathFromId(id$2) ?? id$2;
541
- await Promise.all(extractImportSources(code, id$2).map(async ({ value, start }) => {
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$2) {
556
- const clean = cleanUrl$1(id$2);
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$2) {
560
- const clean = cleanUrl$1(id$2);
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$2) {
571
- const result = parseSync(id$2, code);
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$2) {
591
- return id$2.replace(/[?#].*$/, "");
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$1 = path.relative(root, filePath);
606
- return relative$1 === "" || !!relative$1 && !relative$1.startsWith("..") && !path.isAbsolute(relative$1);
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$2) {
649
- const match = id$2.match(regexSlideSourceId);
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.15.2";
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$1 = join(root, "package.json");
722
- if (!existsSync(path$1)) return {};
723
- const { slidev = {}, engines = {} } = JSON.parse(await fs$1.readFile(path$1, "utf-8"));
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$1 = join(root, "index.html");
765
- if (!existsSync(path$1)) continue;
766
- const html = await readFile(path$1, "utf-8");
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$1)})`));
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" && slash(entry)
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 await transformHtmlTemplate(unhead, main);
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", [{ async loadTheme(path$1) {
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$1, "utf-8"));
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(rootsInfo.userRoot, entry, void 0, mode);
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, "&lbrace;&lbrace;");
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$1] = RE_KATEX_BLOCK_INFO.exec(token.info) || [];
1231
+ const [, rangeStr, options] = RE_KATEX_BLOCK_INFO.exec(token.info) || [];
1232
1232
  const ranges = normalizeRangeStr(rangeStr);
1233
- return `<KaTexBlockWrapper ${options$1 ? `v-bind="${options$1}"` : ""} :ranges='${JSON.stringify(ranges)}'>${katexBlock(tokens[idx].content)}</KaTexBlockWrapper>\n`;
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: id$1,
1251
- async getContent({ data, roots, userRoot }) {
1246
+ id: "/@slidev/conditional-styles",
1247
+ async getContent({ data, roots }) {
1252
1248
  const imports = [];
1253
- for (const root of roots) imports.push(makeAbsoluteImportGlob(id$1, [
1254
- join(root, "styles/index.{ts,js,css}"),
1255
- join(root, "styles.{ts,js,css}"),
1256
- join(root, "style.{ts,js,css}")
1257
- ], {}, userRoot));
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, userRoot }) {
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$1) => join(root, `${name$1}.{ts,js,vue}`));
1288
- yield " Object.values(";
1289
- yield makeAbsoluteImportGlob(id, globs, { import: "default" }, userRoot);
1290
- yield ")[0],\n";
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
- return [
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: id$2,
1408
- getContent({ roots, userRoot }) {
1409
- return `export default [${roots.map((root) => {
1410
- return `Object.values(${makeAbsoluteImportGlob(id$2, [join(root, `setup/${name}.{ts,js,mts,mjs}`)], { import: "default" }, userRoot)})[0]`;
1411
- }).join(", ")}].filter(Boolean)`;
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 setupModules = [
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$1) {
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$1.slides.length; i++) ids[type].push(`${data$1.slides[i].source.filepath}__slidev_${i + 1}.${type}`);
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$2) => ctx.server.moduleGraph.getModuleById(id$2))
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$2) {
1636
- if (id$2.startsWith("/@slidev/") || id$2.includes("__slidev_")) return id$2;
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$2) {
1641
- const template = templates.find((i) => i.id === id$2);
1642
- if (template) return {
1643
- code: await template.getContent.call(this, options),
1644
- map: { mappings: "" }
1645
- };
1646
- const matchFacade = id$2.match(regexSlideFacadeId);
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$2.match(regexSlideSourceId);
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$2]) return "";
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$1) {
1738
+ function withRenderedNote(data) {
1738
1739
  return {
1739
- ...data$1,
1740
+ ...data,
1740
1741
  ...withoutNotes && { note: "" },
1741
- noteHTML: renderNote(data$1?.note)
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=49e14003b6caa0b7d164cbe71da573809d375bab_0dce998d26f79c06311f476731d22630/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/image-size/specialCharacters.js
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$1) {
1773
- SpecialCharacters$1[SpecialCharacters$1["EXCLAMATION_MARK"] = 33] = "EXCLAMATION_MARK";
1774
- SpecialCharacters$1[SpecialCharacters$1["OPENING_BRACKET"] = 91] = "OPENING_BRACKET";
1775
- SpecialCharacters$1[SpecialCharacters$1["OPENING_PARENTHESIS"] = 40] = "OPENING_PARENTHESIS";
1776
- SpecialCharacters$1[SpecialCharacters$1["WHITESPACE"] = 32] = "WHITESPACE";
1777
- SpecialCharacters$1[SpecialCharacters$1["NEW_LINE"] = 10] = "NEW_LINE";
1778
- SpecialCharacters$1[SpecialCharacters$1["EQUALS"] = 61] = "EQUALS";
1779
- SpecialCharacters$1[SpecialCharacters$1["LOWER_CASE_X"] = 120] = "LOWER_CASE_X";
1780
- SpecialCharacters$1[SpecialCharacters$1["NUMBER_ZERO"] = 48] = "NUMBER_ZERO";
1781
- SpecialCharacters$1[SpecialCharacters$1["NUMBER_NINE"] = 57] = "NUMBER_NINE";
1782
- SpecialCharacters$1[SpecialCharacters$1["PERCENTAGE"] = 37] = "PERCENTAGE";
1783
- SpecialCharacters$1[SpecialCharacters$1["CLOSING_PARENTHESIS"] = 41] = "CLOSING_PARENTHESIS";
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.1.1/node_modules/markdown-it/lib/token.mjs
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=49e14003b6caa0b7d164cbe71da573809d375bab_0dce998d26f79c06311f476731d22630/node_modules/@hedgedoc/markdown-it-plugins/dist/esm/task-lists/index.js
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$2 = generateIdForToken(token);
1989
- token.children.splice(0, 0, createCheckboxToken(token, options.enabled, id$2));
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$2));
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$2) {
2001
- const checkbox = new token_default("taskListItemCheckbox", "", 0);
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$2);
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$2) {
2009
- const labelBeginToken = new token_default("taskListItemLabel_open", "", 1);
2010
- labelBeginToken.attrSet("id", id$2);
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 token_default("taskListItemLabel_close", "", -1);
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
- var magic_move_default = defineCodeblockTransformer(async ({ info, fence, code, options: { data: { config }, utils: { shikiOptions, shiki } } }) => {
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 matches = Array.from(code.matchAll(RE_CODE_BLOCK));
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$1 = i[6].trimEnd();
2051
- const options$1 = {
2235
+ const code = i[6].trimEnd();
2236
+ const options = {
2052
2237
  ...shikiOptions,
2053
2238
  lang
2054
2239
  };
2055
- const { tokens, bg, fg: fg$1, rootStyle, themeName } = await shiki.codeToTokens(code$1, options$1);
2240
+ const { tokens, bg, fg, rootStyle, themeName } = await shiki.codeToTokens(code, options);
2056
2241
  return {
2057
- ...toKeyedTokens(code$1, tokens, JSON.stringify([lang, "themes" in options$1 ? options$1.themes : options$1.theme]), lineNumbers),
2242
+ ...toKeyedTokens(code, tokens, JSON.stringify([lang, "themes" in options ? options.themes : options.theme]), lineNumbers),
2058
2243
  bg,
2059
- fg: fg$1,
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$2) {
2162
- const s = id$2 && markdownTransformMap.get(id$2);
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: { handbookOptions: { noErrorValidation: true } }
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$2) {
2552
- if (options.data.markdownFiles[id$2]) return "";
2553
- code = await mdOptions?.transforms?.before?.(code, id$2) ?? code;
2554
- const match = id$2.match(regexSlideSourceId);
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$2, s);
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$2) {
2580
- if (id$2.startsWith("/@slidev-monaco-types/")) return id$2;
3079
+ resolveId(id) {
3080
+ if (id.startsWith("/@slidev-monaco-types/")) return id;
2581
3081
  return null;
2582
3082
  },
2583
- async load(id$2) {
2584
- if (!id$2.startsWith("/@slidev-monaco-types/")) return null;
2585
- const url = new URL(id$2, "http://localhost");
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$1) => !utils.isMonacoTypesIgnored(pkg$1));
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$2) {
2639
- if (!id$2.includes("node_modules/monaco-editor/esm/vs/base")) return;
2640
- return readFile(cleanUrl(id$2), "utf-8");
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$2) => id$2.endsWith("index.html") ? "relative" : "@fs",
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
- export { parser as a, resolveAddons as c, updateFrontmatterPatch as d, resolveOptions as i, version as l, ViteSlidevPlugin as n, getThemeMeta as o, createDataUtils as r, resolveTheme as s, resolveViteConfigs as t, loadSetups as u };
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 };