@ox-content/vite-plugin 2.80.0 → 2.82.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.
Files changed (68) hide show
  1. package/dist/incremental-dom.cjs +3 -2
  2. package/dist/incremental-dom.cjs.map +1 -1
  3. package/dist/incremental-dom.d.cts.map +1 -1
  4. package/dist/incremental-dom.d.mts.map +1 -1
  5. package/dist/incremental-dom.mjs +2 -1
  6. package/dist/incremental-dom.mjs.map +1 -1
  7. package/dist/index.cjs +1486 -99
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +8 -13
  10. package/dist/index.d.cts.map +1 -1
  11. package/dist/index.d.mts +8 -13
  12. package/dist/index.d.mts.map +1 -1
  13. package/dist/index.mjs +1429 -69
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/interop.cjs.map +1 -1
  16. package/dist/interop.mjs.map +1 -1
  17. package/dist/tabs.cjs +3 -4
  18. package/dist/tabs.cjs.map +1 -1
  19. package/dist/tabs.mjs +99 -2
  20. package/dist/tabs.mjs.map +1 -0
  21. package/dist/vitepress-cli.cjs +5 -4
  22. package/dist/vitepress-cli.cjs.map +1 -1
  23. package/dist/vitepress-cli.d.cts +1 -1
  24. package/dist/vitepress-cli.d.mts +1 -1
  25. package/dist/vitepress-cli.mjs +4 -2
  26. package/dist/vitepress-cli.mjs.map +1 -1
  27. package/dist/vitepress.cjs +106 -2
  28. package/dist/vitepress.cjs.map +1 -1
  29. package/dist/vitepress.mjs +32 -2
  30. package/dist/vitepress.mjs.map +1 -1
  31. package/package.json +19 -19
  32. package/dist/chunk.cjs +0 -57
  33. package/dist/github.cjs +0 -690
  34. package/dist/github.cjs.map +0 -1
  35. package/dist/github.mjs +0 -625
  36. package/dist/github.mjs.map +0 -1
  37. package/dist/github2.mjs +0 -2
  38. package/dist/media.cjs +0 -310
  39. package/dist/media.cjs.map +0 -1
  40. package/dist/media.mjs +0 -302
  41. package/dist/media.mjs.map +0 -1
  42. package/dist/media2.mjs +0 -2
  43. package/dist/mermaid.cjs +0 -114
  44. package/dist/mermaid.cjs.map +0 -1
  45. package/dist/mermaid.mjs +0 -2
  46. package/dist/mermaid2.mjs +0 -92
  47. package/dist/mermaid2.mjs.map +0 -1
  48. package/dist/napi.cjs +0 -45
  49. package/dist/napi.cjs.map +0 -1
  50. package/dist/napi.mjs +0 -34
  51. package/dist/napi.mjs.map +0 -1
  52. package/dist/ogp.cjs +0 -341
  53. package/dist/ogp.cjs.map +0 -1
  54. package/dist/ogp.mjs +0 -2
  55. package/dist/ogp2.mjs +0 -302
  56. package/dist/ogp2.mjs.map +0 -1
  57. package/dist/pm.cjs +0 -45
  58. package/dist/pm.cjs.map +0 -1
  59. package/dist/pm.mjs +0 -2
  60. package/dist/pm2.mjs +0 -38
  61. package/dist/pm2.mjs.map +0 -1
  62. package/dist/tabs2.mjs +0 -56
  63. package/dist/tabs2.mjs.map +0 -1
  64. package/dist/youtube.cjs +0 -57
  65. package/dist/youtube.cjs.map +0 -1
  66. package/dist/youtube.mjs +0 -2
  67. package/dist/youtube2.mjs +0 -35
  68. package/dist/youtube2.mjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,37 +1,31 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_chunk = require("./chunk.cjs");
2
+ const require_vitepress = require("./vitepress.cjs");
3
3
  const require_interop = require("./interop.cjs");
4
- const require_napi = require("./napi.cjs");
5
- const require_mermaid = require("./mermaid.cjs");
6
4
  const require_tabs = require("./tabs.cjs");
7
- require("./pm.cjs");
8
- const require_youtube = require("./youtube.cjs");
9
- require("./media.cjs");
10
- const require_github = require("./github.cjs");
11
- const require_ogp = require("./ogp.cjs");
12
- const require_vitepress = require("./vitepress.cjs");
13
5
  let path = require("path");
14
- path = require_chunk.__toESM(path, 1);
6
+ path = require_vitepress.__toESM(path, 1);
15
7
  let unified = require("unified");
16
8
  let rehype_parse = require("rehype-parse");
17
- rehype_parse = require_chunk.__toESM(rehype_parse, 1);
9
+ rehype_parse = require_vitepress.__toESM(rehype_parse, 1);
18
10
  let rehype_stringify = require("rehype-stringify");
19
- rehype_stringify = require_chunk.__toESM(rehype_stringify, 1);
11
+ rehype_stringify = require_vitepress.__toESM(rehype_stringify, 1);
20
12
  let shiki = require("shiki");
21
13
  let node_module = require("node:module");
14
+ let node_fs = require("node:fs");
22
15
  let node_path = require("node:path");
23
- node_path = require_chunk.__toESM(node_path, 1);
16
+ node_path = require_vitepress.__toESM(node_path, 1);
24
17
  let node_fs_promises = require("node:fs/promises");
25
- node_fs_promises = require_chunk.__toESM(node_fs_promises, 1);
18
+ node_fs_promises = require_vitepress.__toESM(node_fs_promises, 1);
19
+ let node_buffer = require("node:buffer");
26
20
  let node_os = require("node:os");
27
21
  let node_util = require("node:util");
28
22
  let node_child_process = require("node:child_process");
29
23
  let fs_promises = require("fs/promises");
30
- fs_promises = require_chunk.__toESM(fs_promises, 1);
24
+ fs_promises = require_vitepress.__toESM(fs_promises, 1);
31
25
  let crypto = require("crypto");
32
- crypto = require_chunk.__toESM(crypto, 1);
26
+ crypto = require_vitepress.__toESM(crypto, 1);
33
27
  let fs = require("fs");
34
- fs = require_chunk.__toESM(fs, 1);
28
+ fs = require_vitepress.__toESM(fs, 1);
35
29
  let glob = require("glob");
36
30
  //#region src/markdown.ts
37
31
  const DEFAULT_MARKDOWN_EXTENSIONS = [
@@ -118,8 +112,8 @@ function createMarkdownEnvironment(options) {
118
112
  /**
119
113
  * Syntax highlighting with Shiki via rehype.
120
114
  */
121
- const rehypeParse$1 = require_interop.interopDefault(rehype_parse.default);
122
- const rehypeStringify$1 = require_interop.interopDefault(rehype_stringify.default);
115
+ const rehypeParse$3 = require_interop.interopDefault(rehype_parse.default);
116
+ const rehypeStringify$3 = require_interop.interopDefault(rehype_stringify.default);
123
117
  const BUILTIN_LANGS = [
124
118
  "javascript",
125
119
  "typescript",
@@ -198,7 +192,7 @@ function rehypeShikiHighlight(options) {
198
192
  lang,
199
193
  theme: themeName
200
194
  });
201
- const parsed = (0, unified.unified)().use(rehypeParse$1, { fragment: true }).parse(highlighted);
195
+ const parsed = (0, unified.unified)().use(rehypeParse$3, { fragment: true }).parse(highlighted);
202
196
  if (parsed.children[0]?.type === "element") {
203
197
  const highlightedPre = parsed.children[0];
204
198
  highlightedPre.properties ??= {};
@@ -220,13 +214,13 @@ function rehypeShikiHighlight(options) {
220
214
  lang,
221
215
  theme: themeName
222
216
  });
223
- const parsed = (0, unified.unified)().use(rehypeParse$1, { fragment: true }).parse(highlighted);
217
+ const parsed = (0, unified.unified)().use(rehypeParse$3, { fragment: true }).parse(highlighted);
224
218
  if (parsed.children[0]?.type === "element") {
225
219
  const highlightedCode = parsed.children[0].children.find((child) => child.type === "element" && child.tagName === "code");
226
220
  if (highlightedCode) {
227
221
  highlightedCode.properties ??= {};
228
222
  const highlightedClasses = normalizeClassName(highlightedCode.properties.className);
229
- highlightedCode.properties.className = [...new Set([
223
+ highlightedCode.properties.className = [.../* @__PURE__ */ new Set([
230
224
  ...originalCodeClasses,
231
225
  ...highlightedClasses,
232
226
  "shiki-inline"
@@ -276,42 +270,1433 @@ function normalizeClassName(className) {
276
270
  * Apply syntax highlighting to HTML using Shiki.
277
271
  */
278
272
  async function highlightCode(html, theme = "github-dark", langs = []) {
279
- const result = await (0, unified.unified)().use(rehypeParse$1, { fragment: true }).use(rehypeShikiHighlight, {
273
+ const result = await (0, unified.unified)().use(rehypeParse$3, { fragment: true }).use(rehypeShikiHighlight, {
280
274
  theme,
281
275
  langs
282
- }).use(rehypeStringify$1).process(html);
276
+ }).use(rehypeStringify$3).process(html);
277
+ return String(result);
278
+ }
279
+ //#endregion
280
+ //#region src/plugins/mermaid.ts
281
+ /**
282
+ * Mermaid Plugin - Native Rust renderer via NAPI
283
+ *
284
+ * Renders mermaid code blocks to SVG using the native Rust renderer
285
+ * via NAPI. Delegates to the NAPI `transformMermaid` function which
286
+ * extracts mermaid code blocks from HTML and renders them using mmdc.
287
+ */
288
+ var mermaid_exports = /* @__PURE__ */ require_vitepress.__exportAll({
289
+ mermaidClientScript: () => "",
290
+ transformMermaidStatic: () => transformMermaidStatic
291
+ });
292
+ /** Cached NAPI bindings */
293
+ let napiBindings$1 = null;
294
+ let napiLoadAttempted$1 = false;
295
+ async function loadNapi() {
296
+ if (napiLoadAttempted$1) return napiBindings$1;
297
+ napiLoadAttempted$1 = true;
298
+ try {
299
+ const binding = await require_vitepress.importNapiModule();
300
+ if (typeof binding.transformMermaid !== "function") {
301
+ napiBindings$1 = null;
302
+ return null;
303
+ }
304
+ napiBindings$1 = binding;
305
+ return binding;
306
+ } catch {
307
+ napiBindings$1 = null;
308
+ return null;
309
+ }
310
+ }
311
+ let cachedMmdcPath;
312
+ let missingMmdcWarned = false;
313
+ function resolveMmdcPath() {
314
+ if (cachedMmdcPath !== void 0) return cachedMmdcPath;
315
+ for (const resolver of createMmdcResolvers()) try {
316
+ const entry = resolver.resolve("@mermaid-js/mermaid-cli");
317
+ const cliPath = (0, node_path.join)((0, node_path.dirname)(entry), "cli.js");
318
+ if ((0, node_fs.existsSync)(cliPath)) {
319
+ cachedMmdcPath = cliPath;
320
+ return cachedMmdcPath;
321
+ }
322
+ } catch {}
323
+ const binPath = (0, node_path.join)(process.cwd(), "node_modules", ".bin", "mmdc");
324
+ if ((0, node_fs.existsSync)(binPath)) {
325
+ cachedMmdcPath = binPath;
326
+ return cachedMmdcPath;
327
+ }
328
+ cachedMmdcPath = null;
329
+ return null;
330
+ }
331
+ function createMmdcResolvers() {
332
+ const consumerRequire = (0, node_module.createRequire)((0, node_path.join)(process.cwd(), "noop.js"));
333
+ const resolvers = [consumerRequire];
334
+ try {
335
+ resolvers.push((0, node_module.createRequire)(consumerRequire.resolve("@ox-content/vite-plugin")));
336
+ } catch {}
337
+ return resolvers;
338
+ }
339
+ /**
340
+ * Transforms mermaid code blocks in HTML to rendered SVG diagrams.
341
+ * Uses the native Rust NAPI transformMermaid function.
342
+ */
343
+ async function transformMermaidStatic(html, _options) {
344
+ const napi = await loadNapi();
345
+ if (!napi) return html;
346
+ const mmdcPath = resolveMmdcPath();
347
+ if (!mmdcPath) {
348
+ warnMissingMmdcOnce();
349
+ return html;
350
+ }
351
+ try {
352
+ const result = napi.transformMermaid(html, mmdcPath);
353
+ for (const error of result.errors) console.warn("[ox-content] Mermaid render error:", error);
354
+ return result.html;
355
+ } catch (err) {
356
+ console.warn("[ox-content] Mermaid transform error:", err);
357
+ return html;
358
+ }
359
+ }
360
+ function warnMissingMmdcOnce() {
361
+ if (missingMmdcWarned) return;
362
+ missingMmdcWarned = true;
363
+ console.warn("[ox-content] mmdc not found; skipping Mermaid rendering.");
364
+ }
365
+ /**
366
+ * @deprecated No longer used. Mermaid rendering is now done at build time via NAPI.
367
+ */
368
+ const mermaidClientScript = "";
369
+ //#endregion
370
+ //#region src/plugins/pm.ts
371
+ /**
372
+ * Package Manager Tabs Plugin
373
+ *
374
+ * Transforms <pm>npm install …</pm> blocks into a tab group with one tab per
375
+ * package manager (npm/pnpm/yarn/bun). The single npm-style command is converted
376
+ * to each package manager's equivalent natively in Rust (`transformPmEmbeds` in
377
+ * @ox-content/napi), and the result reuses the same `ox-tabs` widget markup as
378
+ * the generic `<tabs>` plugin so styling and keyboard navigation are consistent.
379
+ *
380
+ * Syncing is opt-in (off by default): when enabled, the rendered group carries a
381
+ * `data-ox-tab-group="pkg-manager"` attribute so the client runtime can keep
382
+ * every package-manager group on the page in sync via localStorage.
383
+ *
384
+ * Package-manager groups share the tab-group counter with the `<tabs>` plugin so
385
+ * `data-group` ids (and the CSS produced by `generateTabsCSS`) stay unique.
386
+ */
387
+ var pm_exports = /* @__PURE__ */ require_vitepress.__exportAll({ transformPm: () => transformPm });
388
+ /**
389
+ * Transform `<pm>` package-manager blocks in HTML into install tabs.
390
+ *
391
+ * @param html - Rendered HTML potentially containing `<pm>` blocks.
392
+ * @param options - Package-manager tab options (syncing is opt-in).
393
+ * @returns The rewritten HTML.
394
+ */
395
+ async function transformPm(html, options) {
396
+ if (!/<pm[\s/>]/i.test(html)) return html;
397
+ const mod = await require_vitepress.importNapiModule();
398
+ const startGroup = require_tabs.getTabGroupCounter();
399
+ const result = mod.transformPmEmbeds(html, startGroup, { sync: options?.sync ?? false });
400
+ require_tabs.setTabGroupCounter(startGroup + result.groupCount);
401
+ return result.html;
402
+ }
403
+ //#endregion
404
+ //#region src/plugins/youtube.ts
405
+ /**
406
+ * YouTube Plugin - Privacy-enhanced iframe embedding
407
+ *
408
+ * Transforms <YouTube> components into responsive iframe embeds using
409
+ * youtube-nocookie.com for enhanced privacy.
410
+ *
411
+ * The HTML rewrite is performed in Rust (`transformYoutubeEmbeds` in
412
+ * @ox-content/napi), replacing the previous rehype parse/stringify
413
+ * round-trip. This module keeps the public TS surface and a cheap marker
414
+ * check so pages without a `<youtube>` element never cross the NAPI boundary.
415
+ */
416
+ var youtube_exports = /* @__PURE__ */ require_vitepress.__exportAll({
417
+ extractVideoId: () => extractVideoId,
418
+ transformYouTube: () => transformYouTube
419
+ });
420
+ /**
421
+ * Extract YouTube video ID from various URL formats.
422
+ */
423
+ function extractVideoId(input) {
424
+ if (/^[a-zA-Z0-9_-]{11}$/.test(input)) return input;
425
+ for (const pattern of [/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/)([a-zA-Z0-9_-]{11})/, /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/]) {
426
+ const match = input.match(pattern);
427
+ if (match) return match[1];
428
+ }
429
+ return null;
430
+ }
431
+ /**
432
+ * Transform YouTube components in HTML.
433
+ */
434
+ async function transformYouTube(html, options) {
435
+ if (!/<youtube/i.test(html)) return html;
436
+ return (await require_vitepress.importNapiModule()).transformYoutubeEmbeds(html, options);
437
+ }
438
+ //#endregion
439
+ //#region src/plugins/twitter/url.ts
440
+ const STATUS_PATH = /^\/(?:[^/]+|i\/web)\/status\/(\d+)(?:\/.*)?$/;
441
+ function createSyndicationToken(id) {
442
+ return (Number(id) / 0x38d7ea4c68000 * Math.PI).toString(36).replaceAll(/(0+|\.)/g, "");
443
+ }
444
+ function parseTweetReference(value) {
445
+ const trimmed = value.trim();
446
+ if (/^\d+$/.test(trimmed)) return {
447
+ id: trimmed,
448
+ url: `https://x.com/i/web/status/${trimmed}`
449
+ };
450
+ try {
451
+ const url = new URL(trimmed);
452
+ const hostname = url.hostname.toLowerCase().replace(/^(?:www\.|mobile\.)/, "");
453
+ if (url.protocol !== "https:" || hostname !== "x.com" && hostname !== "twitter.com") return null;
454
+ const match = url.pathname.match(STATUS_PATH);
455
+ if (!match) return null;
456
+ const screenName = url.pathname.startsWith("/i/web/status/") ? "i/web" : url.pathname.split("/")[1];
457
+ return {
458
+ id: match[1],
459
+ url: `https://x.com/${screenName}/status/${match[1]}`
460
+ };
461
+ } catch {
462
+ return null;
463
+ }
464
+ }
465
+ function referenceFromAttributes(attributes) {
466
+ const values = /* @__PURE__ */ new Map();
467
+ for (const match of attributes.matchAll(/\b(url|href|id)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi)) values.set(match[1].toLowerCase(), match[2] ?? match[3] ?? match[4] ?? "");
468
+ return parseTweetReference(values.get("url") ?? values.get("href") ?? values.get("id") ?? "");
469
+ }
470
+ //#endregion
471
+ //#region src/plugins/twitter/fetch.ts
472
+ const tweetCache = /* @__PURE__ */ new Map();
473
+ async function fetchTweetData(id, options) {
474
+ const key = `${id}-${sanitizeSegment(options.lang)}`;
475
+ if (options.cache) {
476
+ const memory = tweetCache.get(key);
477
+ if (memory) return memory;
478
+ const disk = await readCachedTweet(key, options.cacheDir);
479
+ if (disk) {
480
+ tweetCache.set(key, disk);
481
+ return disk;
482
+ }
483
+ }
484
+ const controller = new AbortController();
485
+ const timeout = setTimeout(() => controller.abort(), options.timeout);
486
+ const endpoint = new URL("https://cdn.syndication.twimg.com/tweet-result");
487
+ endpoint.searchParams.set("id", id);
488
+ endpoint.searchParams.set("lang", options.lang);
489
+ endpoint.searchParams.set("token", createSyndicationToken(id));
490
+ try {
491
+ const response = await fetch(endpoint, {
492
+ headers: { Accept: "application/json" },
493
+ signal: controller.signal
494
+ });
495
+ if (!response.ok) return null;
496
+ const data = await response.json();
497
+ if (!isTweetData(data)) return null;
498
+ if (options.cache) {
499
+ tweetCache.set(key, data);
500
+ await writeCachedTweet(key, data, options.cacheDir);
501
+ }
502
+ return data;
503
+ } catch {
504
+ return null;
505
+ } finally {
506
+ clearTimeout(timeout);
507
+ }
508
+ }
509
+ async function materializeTweetAssets(id, data, options) {
510
+ const assets = { media: [] };
511
+ const avatarUrl = data.user.profile_image_url_https?.replace(/_normal(?=\.[^.]+$)/, "_bigger");
512
+ if (avatarUrl) assets.avatar = await downloadAsset(avatarUrl, `${id}-avatar`, options);
513
+ const media = data.mediaDetails ?? data.entities?.media ?? [];
514
+ for (const [index, item] of media.entries()) {
515
+ if (item.type && item.type !== "photo") continue;
516
+ if (!item.media_url_https) continue;
517
+ const src = await downloadAsset(item.media_url_https, `${id}-media-${index + 1}`, options);
518
+ if (src) assets.media.push(assetRecord(src, item));
519
+ }
520
+ return assets;
521
+ }
522
+ async function downloadAsset(source, basename, options) {
523
+ let url;
524
+ try {
525
+ url = new URL(source);
526
+ } catch {
527
+ return;
528
+ }
529
+ if (url.protocol !== "https:" || url.hostname.toLowerCase() !== "pbs.twimg.com") return;
530
+ const filename = `${basename}${extensionFromUrl(url)}`;
531
+ const output = node_path.default.join(options.mediaOutputDir, filename);
532
+ try {
533
+ await (0, node_fs_promises.access)(output);
534
+ return joinPublicPath(options.mediaPublicPath, filename);
535
+ } catch {}
536
+ try {
537
+ const response = await fetch(url, { headers: { Accept: "image/*" } });
538
+ if (!response.ok) return void 0;
539
+ await (0, node_fs_promises.mkdir)(options.mediaOutputDir, { recursive: true });
540
+ await (0, node_fs_promises.writeFile)(output, new Uint8Array(await response.arrayBuffer()));
541
+ return joinPublicPath(options.mediaPublicPath, filename);
542
+ } catch {
543
+ return;
544
+ }
545
+ }
546
+ async function readCachedTweet(key, directory) {
547
+ try {
548
+ const data = JSON.parse(await (0, node_fs_promises.readFile)(node_path.default.join(directory, `${key}.json`), "utf8"));
549
+ return isTweetData(data) ? data : null;
550
+ } catch {
551
+ return null;
552
+ }
553
+ }
554
+ async function writeCachedTweet(key, data, directory) {
555
+ try {
556
+ await (0, node_fs_promises.mkdir)(directory, { recursive: true });
557
+ await (0, node_fs_promises.writeFile)(node_path.default.join(directory, `${key}.json`), `${JSON.stringify(data)}\n`);
558
+ } catch {}
559
+ }
560
+ function isTweetData(data) {
561
+ if (!data || typeof data !== "object") return false;
562
+ const value = data;
563
+ return typeof value.text === "string" && Boolean(value.user) && typeof value.user?.name === "string" && typeof value.user.screen_name === "string";
564
+ }
565
+ function extensionFromUrl(url) {
566
+ const match = url.pathname.match(/\.(jpe?g|png|webp|gif)$/i);
567
+ return match ? `.${match[1].toLowerCase().replace("jpeg", "jpg")}` : ".jpg";
568
+ }
569
+ function joinPublicPath(prefix, filename) {
570
+ return `${prefix.replace(/\/$/, "")}/${filename}`;
571
+ }
572
+ function sanitizeSegment(value) {
573
+ return value.replaceAll(/[^a-zA-Z0-9_-]/g, "-");
574
+ }
575
+ function assetRecord(src, media) {
576
+ return {
577
+ src,
578
+ alt: media.ext_alt_text,
579
+ width: media.original_info?.width,
580
+ height: media.original_info?.height
581
+ };
582
+ }
583
+ //#endregion
584
+ //#region src/plugins/twitter/render.ts
585
+ function renderFetchedTweet(permalink, data, assets, options) {
586
+ const profile = `https://x.com/${encodeURIComponent(data.user.screen_name)}`;
587
+ const author = escapeHtml$3(data.user.name);
588
+ const handle = escapeHtml$3(data.user.screen_name);
589
+ const avatar = assets.avatar ? `<img class="ox-tweet__avatar" src="${escapeAttribute(assets.avatar)}" alt="" width="48" height="48" loading="lazy" decoding="async">` : "";
590
+ const media = renderMedia(assets);
591
+ const footer = renderFooter(permalink, data.created_at, options.lang);
592
+ return [
593
+ "<figure class=\"ox-tweet ox-tweet--fetched\">",
594
+ "<header class=\"ox-tweet__header\">",
595
+ `<a class="ox-tweet__profile" href="${escapeAttribute(profile)}" target="_blank" rel="noopener noreferrer">`,
596
+ avatar,
597
+ `<span class="ox-tweet__author-name">${author}</span>`,
598
+ `<span class="ox-tweet__author-handle">@${handle}</span>`,
599
+ "</a></header>",
600
+ `<div class="ox-tweet__body">${renderTweetText(data)}</div>`,
601
+ media,
602
+ footer,
603
+ "</figure>"
604
+ ].join("");
605
+ }
606
+ function renderTweetText(data) {
607
+ const [start, end] = data.display_text_range ?? [0, data.text.length];
608
+ const entities = collectEntities(data).filter((entity) => validRange(entity.indices, start, end)).sort((left, right) => left.indices[0] - right.indices[0]);
609
+ let cursor = start;
610
+ let output = "";
611
+ for (const entity of entities) {
612
+ const [entityStart, entityEnd] = entity.indices;
613
+ if (entityStart < cursor) continue;
614
+ output += escapeText(data.text.slice(cursor, entityStart));
615
+ if (entity.kind === "url") {
616
+ const href = entity.expanded_url ?? entity.url;
617
+ const label = entity.display_url ?? href;
618
+ output += `<a href="${escapeAttribute(href)}" target="_blank" rel="noopener noreferrer">${escapeHtml$3(label)}</a>`;
619
+ }
620
+ cursor = entityEnd;
621
+ }
622
+ output += escapeText(data.text.slice(cursor, end));
623
+ return output.trim();
624
+ }
625
+ function collectEntities(data) {
626
+ return [...(data.entities?.urls ?? []).map((entity) => ({
627
+ ...entity,
628
+ kind: "url"
629
+ })), ...(data.entities?.media ?? []).map((entity) => ({
630
+ ...entity,
631
+ kind: "media"
632
+ }))];
633
+ }
634
+ function validRange(indices, start, end) {
635
+ return Boolean(indices && indices[0] >= start && indices[1] <= end && indices[0] < indices[1]);
636
+ }
637
+ function renderMedia(assets) {
638
+ if (assets.media.length === 0) return "";
639
+ const images = assets.media.map((item) => {
640
+ const size = [item.width ? ` width="${item.width}"` : "", item.height ? ` height="${item.height}"` : ""].join("");
641
+ return `<img class="ox-tweet__media-item" src="${escapeAttribute(item.src)}" alt="${escapeAttribute(item.alt ?? "")}"${size} loading="lazy" decoding="async">`;
642
+ }).join("");
643
+ return `<div class="ox-tweet__media" data-count="${assets.media.length}">${images}</div>`;
644
+ }
645
+ function renderFooter(permalink, createdAt, lang) {
646
+ if (!createdAt) return `<footer class="ox-tweet__footer"><a class="ox-tweet__permalink" href="${escapeAttribute(permalink)}" target="_blank" rel="noopener noreferrer">View on X</a></footer>`;
647
+ const date = new Date(createdAt);
648
+ if (Number.isNaN(date.valueOf())) return renderFooter(permalink, void 0, lang);
649
+ const iso = date.toISOString();
650
+ let label;
651
+ try {
652
+ label = new Intl.DateTimeFormat(lang, {
653
+ dateStyle: "medium",
654
+ timeZone: "UTC"
655
+ }).format(date);
656
+ } catch {
657
+ label = new Intl.DateTimeFormat("en", {
658
+ dateStyle: "medium",
659
+ timeZone: "UTC"
660
+ }).format(date);
661
+ }
662
+ return `<footer class="ox-tweet__footer"><a class="ox-tweet__permalink" href="${escapeAttribute(permalink)}" target="_blank" rel="noopener noreferrer"><time datetime="${iso}">${escapeHtml$3(label)}</time></a></footer>`;
663
+ }
664
+ function escapeText(value) {
665
+ return escapeHtml$3(value).replaceAll("\n", "<br>");
666
+ }
667
+ function escapeAttribute(value) {
668
+ return escapeHtml$3(value).replaceAll("`", "&#96;");
669
+ }
670
+ function escapeHtml$3(value) {
671
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
672
+ }
673
+ //#endregion
674
+ //#region src/plugins/twitter/transform.ts
675
+ const TWEET_ELEMENT = /<(tweet|xpost)\b([^>]*?)(?:\/\s*>|>[\s\S]*?<\/\1\s*>)/gi;
676
+ function resolveTwitterEmbedOptions$1(options) {
677
+ return {
678
+ fetch: options.fetch ?? false,
679
+ lang: options.lang ?? "en",
680
+ timeout: options.timeout ?? 1e4,
681
+ cache: options.cache ?? true,
682
+ cacheDir: node_path.default.resolve(options.cacheDir ?? ".cache/ox-content/twitter"),
683
+ mediaOutputDir: node_path.default.resolve(options.mediaOutputDir ?? "public/ox-content/twitter"),
684
+ mediaPublicPath: options.mediaPublicPath ?? "/ox-content/twitter"
685
+ };
686
+ }
687
+ async function transformFetchedTweets(html, options) {
688
+ const resolved = resolveTwitterEmbedOptions$1(options);
689
+ if (!resolved.fetch) return html;
690
+ let output = "";
691
+ let cursor = 0;
692
+ for (const match of html.matchAll(TWEET_ELEMENT)) {
693
+ const index = match.index ?? 0;
694
+ output += html.slice(cursor, index);
695
+ const reference = referenceFromAttributes(match[2]);
696
+ if (!reference) {
697
+ output += match[0];
698
+ cursor = index + match[0].length;
699
+ continue;
700
+ }
701
+ const data = await fetchTweetData(reference.id, resolved);
702
+ if (!data) {
703
+ output += match[0];
704
+ cursor = index + match[0].length;
705
+ continue;
706
+ }
707
+ const assets = await materializeTweetAssets(reference.id, data, resolved);
708
+ output += renderFetchedTweet(reference.url, data, assets, resolved);
709
+ cursor = index + match[0].length;
710
+ }
711
+ return output + html.slice(cursor);
712
+ }
713
+ //#endregion
714
+ //#region src/plugins/media.ts
715
+ var media_exports = /* @__PURE__ */ require_vitepress.__exportAll({ transformMediaEmbeds: () => transformMediaEmbeds });
716
+ async function transformMediaEmbeds(html, options) {
717
+ if (!hasEnabledMediaEmbed(options) || !hasMediaMarker(html)) return html;
718
+ let result = html;
719
+ if (typeof options.twitter === "object") result = await transformFetchedTweets(result, options.twitter);
720
+ if (!hasMediaMarker(result)) return result;
721
+ return (await require_vitepress.importNapiModule()).transformMediaEmbeds(result, {
722
+ spotify: options.spotify,
723
+ stackBlitz: options.stackBlitz,
724
+ twitter: Boolean(options.twitter),
725
+ bluesky: options.bluesky,
726
+ webContainer: options.webContainer
727
+ });
728
+ }
729
+ function hasEnabledMediaEmbed(options) {
730
+ return Boolean(options.spotify || options.stackBlitz || options.twitter || options.bluesky || options.webContainer);
731
+ }
732
+ function hasMediaMarker(html) {
733
+ return /<(spotify|stackblitz|tweet|xpost|bluesky|webcontainer)[\s/>]/i.test(html);
734
+ }
735
+ //#endregion
736
+ //#region src/plugins/github/validation.ts
737
+ const GITHUB_REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
738
+ function hasControlChar(value) {
739
+ for (let index = 0; index < value.length; index++) {
740
+ const code = value.charCodeAt(index);
741
+ if (code <= 31 || code === 127) return true;
742
+ }
743
+ return false;
744
+ }
745
+ function isSafeGitHubRepo(repo) {
746
+ return GITHUB_REPO_RE.test(repo) && !repo.split("/").some((part) => part === "." || part === "..");
747
+ }
748
+ function isSafeGitHubRef(ref) {
749
+ return Boolean(ref) && !hasControlChar(ref) && !hasUnsafePathSegment(ref);
750
+ }
751
+ function isSafeGitHubPath(path) {
752
+ return Boolean(path) && !hasControlChar(path) && !hasUnsafePathSegment(path);
753
+ }
754
+ function hasUnsafePathSegment(value) {
755
+ return value.split("/").some((part) => !part || part === "." || part === ".." || part.includes("\\"));
756
+ }
757
+ function encodePath(path) {
758
+ return path.split("/").map(encodeURIComponent).join("/");
759
+ }
760
+ //#endregion
761
+ //#region src/plugins/github/source.ts
762
+ const EXTENSION_LANGUAGE_MAP = /* @__PURE__ */ new Map([
763
+ ["cjs", "javascript"],
764
+ ["css", "css"],
765
+ ["go", "go"],
766
+ ["html", "html"],
767
+ ["js", "javascript"],
768
+ ["json", "json"],
769
+ ["jsx", "jsx"],
770
+ ["md", "markdown"],
771
+ ["mdx", "mdx"],
772
+ ["mjs", "javascript"],
773
+ ["py", "python"],
774
+ ["rb", "ruby"],
775
+ ["rs", "rust"],
776
+ ["sh", "shell"],
777
+ ["svelte", "svelte"],
778
+ ["toml", "toml"],
779
+ ["ts", "typescript"],
780
+ ["tsx", "tsx"],
781
+ ["vue", "vue"],
782
+ ["yaml", "yaml"],
783
+ ["yml", "yaml"]
784
+ ]);
785
+ function sourceKey(source) {
786
+ return `${source.repo}@${source.ref}:${source.path}`;
787
+ }
788
+ function formatLineRange(lines) {
789
+ return lines.start === lines.end ? `L${lines.start}` : `L${lines.start}-L${lines.end}`;
790
+ }
791
+ function parseGitHubLineRange(value) {
792
+ if (!value) return void 0;
793
+ const match = value.trim().match(/^#?L?(\d+)(?:-L?(\d+))?$/i);
794
+ if (!match) return void 0;
795
+ const start = Number.parseInt(match[1], 10);
796
+ const end = match[2] ? Number.parseInt(match[2], 10) : start;
797
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 1 || end < start) return;
798
+ return {
799
+ start,
800
+ end
801
+ };
802
+ }
803
+ function createGitHubPermalink(source) {
804
+ const fragment = source.lines ? `#${formatLineRange(source.lines)}` : "";
805
+ return `https://github.com/${source.repo}/blob/${encodeURIComponent(source.ref)}/${encodePath(source.path)}${fragment}`;
806
+ }
807
+ function parseGitHubPermalink(value) {
808
+ let url;
809
+ try {
810
+ url = new URL(value);
811
+ } catch {
812
+ return null;
813
+ }
814
+ if (url.protocol !== "https:" || url.hostname !== "github.com") return null;
815
+ let parts;
816
+ try {
817
+ parts = url.pathname.split("/").filter(Boolean).map((part) => decodeURIComponent(part));
818
+ } catch {
819
+ return null;
820
+ }
821
+ if (parts.length < 5 || parts[2] !== "blob") return null;
822
+ const repo = `${parts[0]}/${parts[1]}`;
823
+ const ref = parts[3];
824
+ const path = parts.slice(4).join("/");
825
+ if (!isSafeGitHubRepo(repo) || !isSafeGitHubRef(ref) || !isSafeGitHubPath(path)) return null;
826
+ const source = {
827
+ repo,
828
+ ref,
829
+ path,
830
+ lines: parseGitHubLineRange(url.hash)
831
+ };
832
+ return {
833
+ ...source,
834
+ permalink: createGitHubPermalink(source)
835
+ };
836
+ }
837
+ function inferLanguage(path) {
838
+ const fileName = path.split("/").at(-1)?.toLowerCase() ?? "";
839
+ if (fileName === "dockerfile") return "dockerfile";
840
+ if (fileName === "makefile") return "makefile";
841
+ const extension = fileName.includes(".") ? fileName.split(".").at(-1) : void 0;
842
+ return extension ? EXTENSION_LANGUAGE_MAP.get(extension) ?? extension : null;
843
+ }
844
+ //#endregion
845
+ //#region src/plugins/github/types.ts
846
+ const defaultOptions$1 = {
847
+ token: "",
848
+ cache: true,
849
+ cacheTTL: 36e5,
850
+ maxSourceBytes: 2e5,
851
+ maxSourceLines: 120
852
+ };
853
+ //#endregion
854
+ //#region src/plugins/github/api.ts
855
+ const repoCache = /* @__PURE__ */ new Map();
856
+ const sourceCache = /* @__PURE__ */ new Map();
857
+ function githubHeaders(options) {
858
+ const headers = {
859
+ Accept: "application/vnd.github.v3+json",
860
+ "User-Agent": "ox-content-github-plugin"
861
+ };
862
+ if (options.token) headers.Authorization = `Bearer ${options.token}`;
863
+ return headers;
864
+ }
865
+ /**
866
+ * Fetch repository data from GitHub API.
867
+ */
868
+ async function fetchRepoData(repo, options) {
869
+ if (!isSafeGitHubRepo(repo)) return null;
870
+ if (options.cache) {
871
+ const cached = repoCache.get(repo);
872
+ if (cached && Date.now() - cached.timestamp < options.cacheTTL) return cached.data;
873
+ }
874
+ try {
875
+ const response = await fetch(`https://api.github.com/repos/${repo}`, { headers: githubHeaders(options) });
876
+ if (!response.ok) {
877
+ console.warn(`Failed to fetch GitHub repo ${repo}: ${response.status}`);
878
+ return null;
879
+ }
880
+ const data = await response.json();
881
+ if (options.cache) repoCache.set(repo, {
882
+ data,
883
+ timestamp: Date.now()
884
+ });
885
+ return data;
886
+ } catch (error) {
887
+ console.warn(`Error fetching GitHub repo ${repo}:`, error);
888
+ return null;
889
+ }
890
+ }
891
+ /**
892
+ * Fetch source file data from GitHub API.
893
+ */
894
+ async function fetchGitHubSource(source, options) {
895
+ if (!isSafeGitHubRepo(source.repo) || !isSafeGitHubRef(source.ref) || !isSafeGitHubPath(source.path)) return null;
896
+ const key = sourceKey(source);
897
+ if (options.cache) {
898
+ const cached = sourceCache.get(key);
899
+ if (cached && Date.now() - cached.timestamp < options.cacheTTL) return cached.data;
900
+ }
901
+ try {
902
+ const apiUrl = `https://api.github.com/repos/${source.repo}/contents/${encodePath(source.path)}?ref=${encodeURIComponent(source.ref)}`;
903
+ const response = await fetch(apiUrl, { headers: githubHeaders(options) });
904
+ if (!response.ok) {
905
+ console.warn(`Failed to fetch GitHub source ${source.permalink}: ${response.status}`);
906
+ return null;
907
+ }
908
+ const data = await response.json();
909
+ if (data.type !== "file" || data.encoding !== "base64" || !data.content || (data.size ?? 0) > options.maxSourceBytes) return null;
910
+ const content = node_buffer.Buffer.from(data.content.replace(/\s/g, ""), "base64").toString("utf8");
911
+ if (node_buffer.Buffer.byteLength(content) > options.maxSourceBytes) return null;
912
+ const sourceData = {
913
+ repo: source.repo,
914
+ ref: source.ref,
915
+ path: source.path,
916
+ permalink: source.permalink,
917
+ content,
918
+ size: data.size ?? node_buffer.Buffer.byteLength(content),
919
+ html_url: data.html_url ?? source.permalink,
920
+ language: inferLanguage(source.path)
921
+ };
922
+ if (options.cache) sourceCache.set(key, {
923
+ data: sourceData,
924
+ timestamp: Date.now()
925
+ });
926
+ return sourceData;
927
+ } catch (error) {
928
+ console.warn(`Error fetching GitHub source ${source.permalink}:`, error);
929
+ return null;
930
+ }
931
+ }
932
+ /**
933
+ * Pre-fetch all GitHub repos data.
934
+ */
935
+ async function prefetchGitHubRepos(repos, options) {
936
+ const mergedOptions = {
937
+ ...defaultOptions$1,
938
+ ...options
939
+ };
940
+ const results = /* @__PURE__ */ new Map();
941
+ await Promise.all(Array.from(new Set(repos)).map(async (repo) => {
942
+ const data = await fetchRepoData(repo, mergedOptions);
943
+ results.set(repo, data);
944
+ }));
945
+ return results;
946
+ }
947
+ /**
948
+ * Pre-fetch all GitHub source files.
949
+ */
950
+ async function prefetchGitHubSources(sources, options) {
951
+ const mergedOptions = {
952
+ ...defaultOptions$1,
953
+ ...options
954
+ };
955
+ const results = /* @__PURE__ */ new Map();
956
+ const uniqueSources = Array.from(new Map(sources.map((source) => [sourceKey(source), source])).values());
957
+ await Promise.all(uniqueSources.map(async (source) => {
958
+ const data = await fetchGitHubSource(source, mergedOptions);
959
+ results.set(sourceKey(source), data);
960
+ }));
961
+ return results;
962
+ }
963
+ //#endregion
964
+ //#region src/plugins/github/attributes.ts
965
+ const GITHUB_COMPONENT_RE = /<github\b([^>]*)>/gi;
966
+ const ATTRIBUTE_RE = /([:\w-]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>/]+)))?/g;
967
+ /**
968
+ * Collect all GitHub repos from HTML for pre-fetching.
969
+ */
970
+ async function collectGitHubRepos(html) {
971
+ const repos = [];
972
+ GITHUB_COMPONENT_RE.lastIndex = 0;
973
+ let match;
974
+ while ((match = GITHUB_COMPONENT_RE.exec(html)) !== null) {
975
+ const attrs = parseAttributes(match[1]);
976
+ if (attrs.path || attrs.file || attrs.permalink || attrs.url || attrs.href) continue;
977
+ const repo = attrs.repo;
978
+ if (repo && isSafeGitHubRepo(repo)) repos.push(repo);
979
+ }
980
+ return repos;
981
+ }
982
+ /**
983
+ * Collect all GitHub source references from HTML for pre-fetching.
984
+ */
985
+ async function collectGitHubSources(html) {
986
+ const sources = [];
987
+ GITHUB_COMPONENT_RE.lastIndex = 0;
988
+ let match;
989
+ while ((match = GITHUB_COMPONENT_RE.exec(html)) !== null) {
990
+ const source = sourceRefFromAttributes(parseAttributes(match[1]));
991
+ if (source) sources.push(source);
992
+ }
993
+ return sources;
994
+ }
995
+ function parseAttributes(raw) {
996
+ const attrs = {};
997
+ ATTRIBUTE_RE.lastIndex = 0;
998
+ let match;
999
+ while ((match = ATTRIBUTE_RE.exec(raw)) !== null) attrs[match[1].toLowerCase()] = match[2] ?? match[3] ?? match[4] ?? "";
1000
+ return attrs;
1001
+ }
1002
+ function attributesFromElement(el) {
1003
+ const attrs = {};
1004
+ for (const name of [
1005
+ "permalink",
1006
+ "url",
1007
+ "href",
1008
+ "repo",
1009
+ "path",
1010
+ "file",
1011
+ "ref",
1012
+ "sha",
1013
+ "branch",
1014
+ "loc",
1015
+ "lines",
1016
+ "line"
1017
+ ]) {
1018
+ const value = getAttribute$2(el, name);
1019
+ if (value !== void 0) attrs[name] = value;
1020
+ }
1021
+ return attrs;
1022
+ }
1023
+ function getAttribute$2(el, name) {
1024
+ const value = el.properties?.[name];
1025
+ if (typeof value === "string") return value;
1026
+ if (Array.isArray(value)) return value.join(" ");
1027
+ }
1028
+ function sourceRefFromAttributes(attrs) {
1029
+ const permalink = attrs.permalink ?? attrs.url ?? attrs.href;
1030
+ if (permalink) return parseGitHubPermalink(permalink);
1031
+ const repo = attrs.repo;
1032
+ const path = attrs.path ?? attrs.file;
1033
+ if (!repo || !path || !isSafeGitHubRepo(repo) || !isSafeGitHubPath(path)) return null;
1034
+ const ref = attrs.ref ?? attrs.sha ?? attrs.branch ?? "main";
1035
+ if (!isSafeGitHubRef(ref)) return null;
1036
+ const source = {
1037
+ repo,
1038
+ ref,
1039
+ path,
1040
+ lines: parseGitHubLineRange(attrs.loc ?? attrs.lines ?? attrs.line)
1041
+ };
1042
+ return {
1043
+ ...source,
1044
+ permalink: createGitHubPermalink(source)
1045
+ };
1046
+ }
1047
+ //#endregion
1048
+ //#region src/plugins/github/fallback-card.ts
1049
+ /**
1050
+ * Create fallback element when repo data is unavailable.
1051
+ */
1052
+ function createFallbackCard$1(repo) {
1053
+ return {
1054
+ type: "element",
1055
+ tagName: "a",
1056
+ properties: {
1057
+ className: ["ox-github-card", "error"],
1058
+ href: isSafeGitHubRepo(repo) ? `https://github.com/${repo}` : "#",
1059
+ target: "_blank",
1060
+ rel: "noopener noreferrer"
1061
+ },
1062
+ children: [{
1063
+ type: "element",
1064
+ tagName: "div",
1065
+ properties: { className: ["ox-github-header"] },
1066
+ children: [{
1067
+ type: "element",
1068
+ tagName: "svg",
1069
+ properties: {
1070
+ className: ["ox-github-icon"],
1071
+ viewBox: "0 0 16 16",
1072
+ fill: "currentColor"
1073
+ },
1074
+ children: [{
1075
+ type: "element",
1076
+ tagName: "path",
1077
+ properties: { d: "M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z" },
1078
+ children: []
1079
+ }]
1080
+ }, {
1081
+ type: "element",
1082
+ tagName: "span",
1083
+ properties: { className: ["ox-github-repo"] },
1084
+ children: [{
1085
+ type: "text",
1086
+ value: repo
1087
+ }]
1088
+ }]
1089
+ }]
1090
+ };
1091
+ }
1092
+ //#endregion
1093
+ //#region src/plugins/github/repo-card.ts
1094
+ function formatNumber(num) {
1095
+ if (num >= 1e6) return `${(num / 1e6).toFixed(1)}M`;
1096
+ if (num >= 1e3) return `${(num / 1e3).toFixed(1)}k`;
1097
+ return String(num);
1098
+ }
1099
+ function iconPath(d) {
1100
+ return {
1101
+ type: "element",
1102
+ tagName: "svg",
1103
+ properties: {
1104
+ viewBox: "0 0 16 16",
1105
+ fill: "currentColor"
1106
+ },
1107
+ children: [{
1108
+ type: "element",
1109
+ tagName: "path",
1110
+ properties: { d },
1111
+ children: []
1112
+ }]
1113
+ };
1114
+ }
1115
+ function createStatsChildren(repoData) {
1116
+ const statsChildren = [];
1117
+ if (repoData.language) statsChildren.push({
1118
+ type: "element",
1119
+ tagName: "span",
1120
+ properties: { className: ["ox-github-language"] },
1121
+ children: [{
1122
+ type: "element",
1123
+ tagName: "span",
1124
+ properties: {
1125
+ className: ["ox-github-language-color"],
1126
+ "data-lang": repoData.language.toLowerCase()
1127
+ },
1128
+ children: []
1129
+ }, {
1130
+ type: "text",
1131
+ value: repoData.language
1132
+ }]
1133
+ });
1134
+ statsChildren.push({
1135
+ type: "element",
1136
+ tagName: "span",
1137
+ properties: { className: ["ox-github-stat"] },
1138
+ children: [iconPath("M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"), {
1139
+ type: "text",
1140
+ value: formatNumber(repoData.stargazers_count)
1141
+ }]
1142
+ });
1143
+ statsChildren.push({
1144
+ type: "element",
1145
+ tagName: "span",
1146
+ properties: { className: ["ox-github-stat"] },
1147
+ children: [iconPath("M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"), {
1148
+ type: "text",
1149
+ value: formatNumber(repoData.forks_count)
1150
+ }]
1151
+ });
1152
+ return statsChildren;
1153
+ }
1154
+ /**
1155
+ * Create GitHub card element from repo data.
1156
+ */
1157
+ function createGitHubCard(repoData) {
1158
+ return {
1159
+ type: "element",
1160
+ tagName: "a",
1161
+ properties: {
1162
+ className: ["ox-github-card"],
1163
+ href: repoData.html_url,
1164
+ target: "_blank",
1165
+ rel: "noopener noreferrer"
1166
+ },
1167
+ children: [
1168
+ {
1169
+ type: "element",
1170
+ tagName: "div",
1171
+ properties: { className: ["ox-github-header"] },
1172
+ children: [{
1173
+ ...iconPath("M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"),
1174
+ properties: {
1175
+ className: ["ox-github-icon"],
1176
+ viewBox: "0 0 16 16",
1177
+ fill: "currentColor"
1178
+ }
1179
+ }, {
1180
+ type: "element",
1181
+ tagName: "span",
1182
+ properties: { className: ["ox-github-repo"] },
1183
+ children: [{
1184
+ type: "text",
1185
+ value: repoData.full_name
1186
+ }]
1187
+ }]
1188
+ },
1189
+ ...repoData.description ? [{
1190
+ type: "element",
1191
+ tagName: "p",
1192
+ properties: { className: ["ox-github-description"] },
1193
+ children: [{
1194
+ type: "text",
1195
+ value: repoData.description
1196
+ }]
1197
+ }] : [],
1198
+ {
1199
+ type: "element",
1200
+ tagName: "div",
1201
+ properties: { className: ["ox-github-stats"] },
1202
+ children: createStatsChildren(repoData)
1203
+ }
1204
+ ]
1205
+ };
1206
+ }
1207
+ //#endregion
1208
+ //#region src/plugins/github/source-card.ts
1209
+ function normalizeSourceLines(content) {
1210
+ const lines = content.replace(/\r\n?/g, "\n").split("\n");
1211
+ if (lines.length > 1 && lines.at(-1) === "") lines.pop();
1212
+ return lines.length > 0 ? lines : [""];
1213
+ }
1214
+ function createGitHubSourceCard(source, lines, options) {
1215
+ const allLines = normalizeSourceLines(source.content);
1216
+ const start = Math.min(lines?.start ?? 1, allLines.length);
1217
+ const end = lines ? Math.min(lines.end, allLines.length) : Math.min(allLines.length, options.maxSourceLines);
1218
+ const selectedLines = allLines.slice(start - 1, end);
1219
+ const lineRange = {
1220
+ start,
1221
+ end
1222
+ };
1223
+ const loc = selectedLines.length;
1224
+ const rangeLabel = formatLineRange(lineRange);
1225
+ const locLabel = !lines && end < allLines.length ? `${rangeLabel} of ${allLines.length} LOC` : `${rangeLabel} - ${loc} LOC`;
1226
+ const languageClass = source.language ? [`language-${source.language}`] : [];
1227
+ return {
1228
+ type: "element",
1229
+ tagName: "figure",
1230
+ properties: {
1231
+ className: ["ox-github-code"],
1232
+ "data-loc": String(loc),
1233
+ "data-source": source.permalink
1234
+ },
1235
+ children: [{
1236
+ type: "element",
1237
+ tagName: "figcaption",
1238
+ properties: { className: ["ox-github-code-header"] },
1239
+ children: [{
1240
+ type: "element",
1241
+ tagName: "a",
1242
+ properties: {
1243
+ className: ["ox-github-code-title"],
1244
+ href: source.permalink,
1245
+ target: "_blank",
1246
+ rel: "noopener noreferrer"
1247
+ },
1248
+ children: [{
1249
+ type: "text",
1250
+ value: `${source.repo}/${source.path}`
1251
+ }]
1252
+ }, {
1253
+ type: "element",
1254
+ tagName: "span",
1255
+ properties: { className: ["ox-github-code-loc"] },
1256
+ children: [{
1257
+ type: "text",
1258
+ value: locLabel
1259
+ }]
1260
+ }]
1261
+ }, {
1262
+ type: "element",
1263
+ tagName: "pre",
1264
+ properties: {
1265
+ className: ["ox-github-code-block", ...languageClass],
1266
+ ...source.language ? { "data-language": source.language } : {}
1267
+ },
1268
+ children: [{
1269
+ type: "element",
1270
+ tagName: "code",
1271
+ properties: { className: languageClass },
1272
+ children: selectedLines.map((line, index) => {
1273
+ const lineNumber = start + index;
1274
+ return {
1275
+ type: "element",
1276
+ tagName: "span",
1277
+ properties: {
1278
+ className: ["line", "ox-github-code-line"],
1279
+ "data-line": String(lineNumber)
1280
+ },
1281
+ children: [{
1282
+ type: "element",
1283
+ tagName: "span",
1284
+ properties: { className: ["ox-github-code-line-number"] },
1285
+ children: [{
1286
+ type: "text",
1287
+ value: String(lineNumber)
1288
+ }]
1289
+ }, {
1290
+ type: "element",
1291
+ tagName: "span",
1292
+ properties: { className: ["ox-github-code-line-content"] },
1293
+ children: [{
1294
+ type: "text",
1295
+ value: line || " "
1296
+ }]
1297
+ }]
1298
+ };
1299
+ })
1300
+ }]
1301
+ }]
1302
+ };
1303
+ }
1304
+ //#endregion
1305
+ //#region src/plugins/github/transform.ts
1306
+ const rehypeParse$2 = require_interop.interopDefault(rehype_parse.default);
1307
+ const rehypeStringify$2 = require_interop.interopDefault(rehype_stringify.default);
1308
+ /**
1309
+ * Rehype plugin to transform GitHub components.
1310
+ */
1311
+ function rehypeGitHub(repoDataMap, sourceDataMap, options) {
1312
+ return (tree) => {
1313
+ const visit = (node) => {
1314
+ if ("children" in node) for (let i = 0; i < node.children.length; i++) {
1315
+ const child = node.children[i];
1316
+ if (child.type !== "element") continue;
1317
+ if (child.tagName.toLowerCase() !== "github") {
1318
+ visit(child);
1319
+ continue;
1320
+ }
1321
+ const attrs = attributesFromElement(child);
1322
+ const source = sourceRefFromAttributes(attrs);
1323
+ if (source) {
1324
+ const sourceData = sourceDataMap.get(sourceKey(source));
1325
+ node.children[i] = sourceData ? createGitHubSourceCard(sourceData, source.lines, options) : createFallbackCard$1(source.permalink);
1326
+ continue;
1327
+ }
1328
+ const repo = attrs.repo;
1329
+ if (repo) {
1330
+ const repoData = repoDataMap.get(repo);
1331
+ node.children[i] = repoData ? createGitHubCard(repoData) : createFallbackCard$1(repo);
1332
+ }
1333
+ }
1334
+ };
1335
+ visit(tree);
1336
+ };
1337
+ }
1338
+ /**
1339
+ * Transform GitHub components in HTML.
1340
+ */
1341
+ async function transformGitHub(html, repoDataMap, options) {
1342
+ const mergedOptions = {
1343
+ ...defaultOptions$1,
1344
+ ...options
1345
+ };
1346
+ let dataMap = repoDataMap;
1347
+ if (!dataMap) dataMap = await prefetchGitHubRepos(await collectGitHubRepos(html), mergedOptions);
1348
+ const sourceDataMap = await prefetchGitHubSources(await collectGitHubSources(html), mergedOptions);
1349
+ const result = await (0, unified.unified)().use(rehypeParse$2, { fragment: true }).use(rehypeGitHub, dataMap, sourceDataMap, mergedOptions).use(rehypeStringify$2).process(html);
1350
+ return String(result);
1351
+ }
1352
+ //#endregion
1353
+ //#region src/plugins/github.ts
1354
+ var github_exports = /* @__PURE__ */ require_vitepress.__exportAll({ transformGitHub: () => transformGitHub });
1355
+ //#endregion
1356
+ //#region src/plugins/ogp.ts
1357
+ /**
1358
+ * OGP Card Plugin - Link card embedding
1359
+ *
1360
+ * Transforms <OgCard> components into static link preview cards
1361
+ * by fetching OGP metadata at build time.
1362
+ */
1363
+ var ogp_exports = /* @__PURE__ */ require_vitepress.__exportAll({
1364
+ collectOgpUrls: () => collectOgpUrls,
1365
+ fetchOgpData: () => fetchOgpData,
1366
+ isSafeOgpUrl: () => isSafeOgpUrl,
1367
+ prefetchOgpData: () => prefetchOgpData,
1368
+ transformOgp: () => transformOgp
1369
+ });
1370
+ const rehypeParse$1 = require_interop.interopDefault(rehype_parse.default);
1371
+ const rehypeStringify$1 = require_interop.interopDefault(rehype_stringify.default);
1372
+ const defaultOptions = {
1373
+ timeout: 1e4,
1374
+ cache: true,
1375
+ cacheTTL: 36e5,
1376
+ userAgent: "ox-content-ogp-bot/1.0 (compatible; +https://github.com/ubugeeei-prod/ox-content)"
1377
+ };
1378
+ const ogpCache = /* @__PURE__ */ new Map();
1379
+ function isPrivateIPv4(hostname) {
1380
+ const parts = hostname.split(".").map(Number);
1381
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return false;
1382
+ const [a, b] = parts;
1383
+ return a === 10 || a === 127 || a === 0 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254;
1384
+ }
1385
+ function isSafeOgpUrl(value) {
1386
+ try {
1387
+ const url = new URL(value);
1388
+ const host = url.hostname.toLowerCase();
1389
+ const ipv6 = host.replace(/^\[|\]$/g, "");
1390
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
1391
+ if (host === "localhost" || host.endsWith(".localhost")) return false;
1392
+ if (ipv6.includes(":") && (ipv6 === "::1" || ipv6.startsWith("fc") || ipv6.startsWith("fd") || ipv6.startsWith("fe80"))) return false;
1393
+ return !isPrivateIPv4(host);
1394
+ } catch {
1395
+ return false;
1396
+ }
1397
+ }
1398
+ /**
1399
+ * Get element attribute value.
1400
+ */
1401
+ function getAttribute$1(el, name) {
1402
+ const value = el.properties?.[name];
1403
+ if (typeof value === "string") return value;
1404
+ if (Array.isArray(value)) return value.join(" ");
1405
+ }
1406
+ /**
1407
+ * Extract domain from URL.
1408
+ */
1409
+ function extractDomain(url) {
1410
+ try {
1411
+ return new URL(url).hostname;
1412
+ } catch {
1413
+ return url;
1414
+ }
1415
+ }
1416
+ /**
1417
+ * Get favicon URL for a domain.
1418
+ */
1419
+ function getFaviconUrl(url) {
1420
+ try {
1421
+ return `https://www.google.com/s2/favicons?domain=${new URL(url).hostname}&sz=32`;
1422
+ } catch {
1423
+ return "";
1424
+ }
1425
+ }
1426
+ /**
1427
+ * Parse OGP metadata from HTML.
1428
+ */
1429
+ function parseOgpFromHtml(html, url) {
1430
+ const result = {
1431
+ url,
1432
+ title: ""
1433
+ };
1434
+ const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i);
1435
+ result.title = (html.match(/<meta[^>]*property=["']og:title["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:title["']/i))?.[1] || titleMatch?.[1] || extractDomain(url);
1436
+ const descMatch = html.match(/<meta[^>]*property=["']og:description["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:description["']/i) || html.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*name=["']description["']/i);
1437
+ if (descMatch) result.description = descMatch[1];
1438
+ const imageMatch = html.match(/<meta[^>]*property=["']og:image["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:image["']/i);
1439
+ if (imageMatch) {
1440
+ let imageUrl = imageMatch[1];
1441
+ if (imageUrl.startsWith("/")) try {
1442
+ const urlObj = new URL(url);
1443
+ imageUrl = `${urlObj.protocol}//${urlObj.host}${imageUrl}`;
1444
+ } catch {}
1445
+ result.image = imageUrl;
1446
+ }
1447
+ const siteNameMatch = html.match(/<meta[^>]*property=["']og:site_name["'][^>]*content=["']([^"']+)["']/i) || html.match(/<meta[^>]*content=["']([^"']+)["'][^>]*property=["']og:site_name["']/i);
1448
+ if (siteNameMatch) result.siteName = siteNameMatch[1];
1449
+ result.favicon = getFaviconUrl(url);
1450
+ return result;
1451
+ }
1452
+ /**
1453
+ * Fetch OGP data for a URL.
1454
+ */
1455
+ async function fetchOgpData(url, options) {
1456
+ if (!isSafeOgpUrl(url)) return null;
1457
+ if (options.cache) {
1458
+ const cached = ogpCache.get(url);
1459
+ if (cached && Date.now() - cached.timestamp < options.cacheTTL) return cached.data;
1460
+ }
1461
+ try {
1462
+ const controller = new AbortController();
1463
+ const timeoutId = setTimeout(() => controller.abort(), options.timeout);
1464
+ const response = await fetch(url, {
1465
+ headers: {
1466
+ "User-Agent": options.userAgent,
1467
+ Accept: "text/html,application/xhtml+xml"
1468
+ },
1469
+ signal: controller.signal
1470
+ });
1471
+ clearTimeout(timeoutId);
1472
+ if (!response.ok) {
1473
+ console.warn(`Failed to fetch OGP for ${url}: ${response.status}`);
1474
+ return null;
1475
+ }
1476
+ const data = parseOgpFromHtml(await response.text(), url);
1477
+ if (options.cache) ogpCache.set(url, {
1478
+ data,
1479
+ timestamp: Date.now()
1480
+ });
1481
+ return data;
1482
+ } catch (error) {
1483
+ if (error instanceof Error && error.name === "AbortError") console.warn(`Timeout fetching OGP for ${url}`);
1484
+ else console.warn(`Error fetching OGP for ${url}:`, error);
1485
+ return null;
1486
+ }
1487
+ }
1488
+ /**
1489
+ * Create OGP card element.
1490
+ */
1491
+ function createOgpCard(data) {
1492
+ const children = [];
1493
+ const contentChildren = [];
1494
+ contentChildren.push({
1495
+ type: "element",
1496
+ tagName: "div",
1497
+ properties: { className: ["ox-ogp-title"] },
1498
+ children: [{
1499
+ type: "text",
1500
+ value: data.title
1501
+ }]
1502
+ });
1503
+ if (data.description) contentChildren.push({
1504
+ type: "element",
1505
+ tagName: "div",
1506
+ properties: { className: ["ox-ogp-description"] },
1507
+ children: [{
1508
+ type: "text",
1509
+ value: data.description
1510
+ }]
1511
+ });
1512
+ const metaChildren = [];
1513
+ if (data.favicon) metaChildren.push({
1514
+ type: "element",
1515
+ tagName: "img",
1516
+ properties: {
1517
+ className: ["ox-ogp-favicon"],
1518
+ src: data.favicon,
1519
+ alt: "",
1520
+ loading: "lazy"
1521
+ },
1522
+ children: []
1523
+ });
1524
+ metaChildren.push({
1525
+ type: "element",
1526
+ tagName: "span",
1527
+ properties: { className: ["ox-ogp-domain"] },
1528
+ children: [{
1529
+ type: "text",
1530
+ value: data.siteName || extractDomain(data.url)
1531
+ }]
1532
+ });
1533
+ contentChildren.push({
1534
+ type: "element",
1535
+ tagName: "div",
1536
+ properties: { className: ["ox-ogp-meta"] },
1537
+ children: metaChildren
1538
+ });
1539
+ children.push({
1540
+ type: "element",
1541
+ tagName: "div",
1542
+ properties: { className: ["ox-ogp-content"] },
1543
+ children: contentChildren
1544
+ });
1545
+ if (data.image) children.push({
1546
+ type: "element",
1547
+ tagName: "img",
1548
+ properties: {
1549
+ className: ["ox-ogp-image"],
1550
+ src: data.image,
1551
+ alt: "",
1552
+ loading: "lazy"
1553
+ },
1554
+ children: []
1555
+ });
1556
+ return {
1557
+ type: "element",
1558
+ tagName: "a",
1559
+ properties: {
1560
+ className: ["ox-ogp-card"],
1561
+ href: isSafeOgpUrl(data.url) ? data.url : "#",
1562
+ target: "_blank",
1563
+ rel: "noopener noreferrer"
1564
+ },
1565
+ children
1566
+ };
1567
+ }
1568
+ /**
1569
+ * Create fallback element when OGP data is unavailable.
1570
+ */
1571
+ function createFallbackCard(url) {
1572
+ return {
1573
+ type: "element",
1574
+ tagName: "a",
1575
+ properties: {
1576
+ className: ["ox-ogp-simple"],
1577
+ href: isSafeOgpUrl(url) ? url : "#",
1578
+ target: "_blank",
1579
+ rel: "noopener noreferrer"
1580
+ },
1581
+ children: [{
1582
+ type: "element",
1583
+ tagName: "svg",
1584
+ properties: {
1585
+ viewBox: "0 0 24 24",
1586
+ fill: "none",
1587
+ stroke: "currentColor",
1588
+ "stroke-width": "2"
1589
+ },
1590
+ children: [{
1591
+ type: "element",
1592
+ tagName: "path",
1593
+ properties: { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14L21 3" },
1594
+ children: []
1595
+ }]
1596
+ }, {
1597
+ type: "text",
1598
+ value: extractDomain(url)
1599
+ }]
1600
+ };
1601
+ }
1602
+ /**
1603
+ * Collect all OGP URLs from HTML for pre-fetching.
1604
+ */
1605
+ async function collectOgpUrls(html) {
1606
+ const urls = [];
1607
+ const urlPattern = /<ogcard[^>]*\s+url=["']([^"']+)["']/gi;
1608
+ let match;
1609
+ while ((match = urlPattern.exec(html)) !== null) if (isSafeOgpUrl(match[1])) urls.push(match[1]);
1610
+ return urls;
1611
+ }
1612
+ /**
1613
+ * Pre-fetch all OGP data.
1614
+ */
1615
+ async function prefetchOgpData(urls, options) {
1616
+ const mergedOptions = {
1617
+ ...defaultOptions,
1618
+ ...options
1619
+ };
1620
+ const results = /* @__PURE__ */ new Map();
1621
+ await Promise.all(urls.map(async (url) => {
1622
+ const data = await fetchOgpData(url, mergedOptions);
1623
+ results.set(url, data);
1624
+ }));
1625
+ return results;
1626
+ }
1627
+ /**
1628
+ * Rehype plugin to transform OgCard components.
1629
+ */
1630
+ function rehypeOgp(ogpDataMap) {
1631
+ return (tree) => {
1632
+ const visit = (node) => {
1633
+ if ("children" in node) for (let i = 0; i < node.children.length; i++) {
1634
+ const child = node.children[i];
1635
+ if (child.type === "element") if (child.tagName.toLowerCase() === "ogcard") {
1636
+ const url = getAttribute$1(child, "url");
1637
+ if (url) {
1638
+ const ogpData = ogpDataMap.get(url);
1639
+ const cardElement = ogpData ? createOgpCard(ogpData) : createFallbackCard(url);
1640
+ node.children[i] = cardElement;
1641
+ }
1642
+ } else visit(child);
1643
+ }
1644
+ };
1645
+ visit(tree);
1646
+ };
1647
+ }
1648
+ /**
1649
+ * Transform OgCard components in HTML.
1650
+ */
1651
+ async function transformOgp(html, ogpDataMap, options) {
1652
+ let dataMap = ogpDataMap;
1653
+ if (!dataMap) dataMap = await prefetchOgpData(await collectOgpUrls(html), options);
1654
+ const result = await (0, unified.unified)().use(rehypeParse$1, { fragment: true }).use(rehypeOgp, dataMap).use(rehypeStringify$1).process(html);
283
1655
  return String(result);
284
1656
  }
285
1657
  //#endregion
286
1658
  //#region src/plugins/index.ts
1659
+ const SELF_CLOSING_EMBED_TAG = /<(GitHub|OgCard|Tweet|XPost|Bluesky|Spotify|StackBlitz|WebContainer|YouTube)((?:[^>"']|"[^"]*"|'[^']*')*?)\s*\/>/gi;
1660
+ /**
1661
+ * Custom embed tags are not HTML void elements, so a self-closing authoring
1662
+ * form like `<GitHub ... />` reaches the HTML re-parsers (Shiki highlighting,
1663
+ * embed transforms) as an unclosed element that swallows the rest of the
1664
+ * document. Normalize to an explicit open/close pair before any rehype pass
1665
+ * runs.
1666
+ */
1667
+ function normalizeSelfClosingEmbeds(html) {
1668
+ return html.replace(SELF_CLOSING_EMBED_TAG, (_match, tag, attrs) => {
1669
+ return `<${tag}${attrs}></${tag}>`;
1670
+ });
1671
+ }
287
1672
  /**
288
1673
  * Transform all enabled plugins in HTML content.
289
1674
  */
290
1675
  async function transformAllPlugins(html, options = {}) {
291
1676
  const { tabs = true, pm = false, youtube = true, github = true, ogp, openGraph, mermaid = true, githubToken, spotify = false, stackBlitz = false, twitter = false, bluesky = false, webContainer = false } = options;
292
- let result = html;
1677
+ let result = normalizeSelfClosingEmbeds(html);
293
1678
  const ogpOptions = openGraph ?? ogp ?? true;
294
1679
  if (tabs) {
295
1680
  const { transformTabs } = await Promise.resolve().then(() => require("./tabs.cjs")).then((n) => n.tabs_exports);
296
1681
  result = await transformTabs(result);
297
1682
  }
298
1683
  if (pm) {
299
- const { transformPm } = await Promise.resolve().then(() => require("./pm.cjs")).then((n) => n.pm_exports);
1684
+ const { transformPm } = await Promise.resolve().then(() => pm_exports);
300
1685
  result = await transformPm(result, typeof pm === "object" ? pm : {});
301
1686
  }
302
1687
  if (youtube) {
303
- const { transformYouTube } = await Promise.resolve().then(() => require("./youtube.cjs")).then((n) => n.youtube_exports);
1688
+ const { transformYouTube } = await Promise.resolve().then(() => youtube_exports);
304
1689
  result = await transformYouTube(result);
305
1690
  }
306
1691
  if (github !== false) {
307
- const { transformGitHub } = await Promise.resolve().then(() => require("./github.cjs")).then((n) => n.github_exports);
1692
+ const { transformGitHub } = await Promise.resolve().then(() => github_exports);
308
1693
  result = await transformGitHub(result, void 0, {
309
1694
  token: githubToken,
310
1695
  ...typeof github === "object" ? github : {}
311
1696
  });
312
1697
  }
313
1698
  if (ogpOptions !== false) {
314
- const { transformOgp } = await Promise.resolve().then(() => require("./ogp.cjs")).then((n) => n.ogp_exports);
1699
+ const { transformOgp } = await Promise.resolve().then(() => ogp_exports);
315
1700
  result = await transformOgp(result, void 0, typeof ogpOptions === "object" ? ogpOptions : {});
316
1701
  }
317
1702
  const mediaOptions = {
@@ -322,11 +1707,11 @@ async function transformAllPlugins(html, options = {}) {
322
1707
  webContainer
323
1708
  };
324
1709
  if (Object.values(mediaOptions).some(Boolean)) {
325
- const { transformMediaEmbeds } = await Promise.resolve().then(() => require("./media.cjs")).then((n) => n.media_exports);
1710
+ const { transformMediaEmbeds } = await Promise.resolve().then(() => media_exports);
326
1711
  result = await transformMediaEmbeds(result, mediaOptions);
327
1712
  }
328
1713
  if (mermaid) {
329
- const { transformMermaidStatic } = await Promise.resolve().then(() => require("./mermaid.cjs")).then((n) => n.mermaid_exports);
1714
+ const { transformMermaidStatic } = await Promise.resolve().then(() => mermaid_exports);
330
1715
  result = await transformMermaidStatic(result);
331
1716
  }
332
1717
  return result;
@@ -335,20 +1720,20 @@ async function transformAllPlugins(html, options = {}) {
335
1720
  * Transform built-in embed components in HTML content.
336
1721
  */
337
1722
  async function transformBuiltinEmbeds(html, options) {
338
- let result = html;
1723
+ let result = normalizeSelfClosingEmbeds(html);
339
1724
  if (options.github) {
340
- const { transformGitHub } = await Promise.resolve().then(() => require("./github.cjs")).then((n) => n.github_exports);
1725
+ const { transformGitHub } = await Promise.resolve().then(() => github_exports);
341
1726
  result = await transformGitHub(result, void 0, {
342
1727
  token: process.env.GITHUB_TOKEN,
343
1728
  ...options.github
344
1729
  });
345
1730
  }
346
1731
  if (options.openGraph) {
347
- const { transformOgp } = await Promise.resolve().then(() => require("./ogp.cjs")).then((n) => n.ogp_exports);
1732
+ const { transformOgp } = await Promise.resolve().then(() => ogp_exports);
348
1733
  result = await transformOgp(result, void 0, options.openGraph);
349
1734
  }
350
1735
  if (options.pm) {
351
- const { transformPm } = await Promise.resolve().then(() => require("./pm.cjs")).then((n) => n.pm_exports);
1736
+ const { transformPm } = await Promise.resolve().then(() => pm_exports);
352
1737
  result = await transformPm(result, typeof options.pm === "object" ? options.pm : {});
353
1738
  }
354
1739
  const mediaOptions = {
@@ -359,7 +1744,7 @@ async function transformBuiltinEmbeds(html, options) {
359
1744
  webContainer: options.webContainer
360
1745
  };
361
1746
  if (Object.values(mediaOptions).some(Boolean)) {
362
- const { transformMediaEmbeds } = await Promise.resolve().then(() => require("./media.cjs")).then((n) => n.media_exports);
1747
+ const { transformMediaEmbeds } = await Promise.resolve().then(() => media_exports);
363
1748
  result = await transformMediaEmbeds(result, mediaOptions);
364
1749
  }
365
1750
  return result;
@@ -422,10 +1807,10 @@ function restoreMermaidSvgs(html, svgs) {
422
1807
  //#region src/code-blocks.ts
423
1808
  const execFileAsync = (0, node_util.promisify)(node_child_process.execFile);
424
1809
  async function extractCodeBlocks(source) {
425
- return (await require_napi.importNapiModule()).extractCodeBlocks(source).map(normalizeBlock);
1810
+ return (await require_vitepress.importNapiModule()).extractCodeBlocks(source).map(normalizeBlock);
426
1811
  }
427
1812
  async function lintCodeBlocks(source, options = {}) {
428
- return (await require_napi.importNapiModule()).lintCodeBlocks(source, {
1813
+ return (await require_vitepress.importNapiModule()).lintCodeBlocks(source, {
429
1814
  enabled: true,
430
1815
  languages: options.languages,
431
1816
  requireLanguage: options.requireLanguage,
@@ -433,7 +1818,7 @@ async function lintCodeBlocks(source, options = {}) {
433
1818
  }).map(normalizeDiagnostic);
434
1819
  }
435
1820
  async function extractDocsTests(source, options = {}) {
436
- return (await require_napi.importNapiModule()).extractDocsTests(source, {
1821
+ return (await require_vitepress.importNapiModule()).extractDocsTests(source, {
437
1822
  enabled: true,
438
1823
  languages: options.languages,
439
1824
  requireMeta: options.requireMeta
@@ -451,7 +1836,8 @@ async function typecheckCodeBlocks(source, options = {}) {
451
1836
  try {
452
1837
  const files = [];
453
1838
  await Promise.all(blocks.map(async (block, index) => {
454
- const file = (0, node_path.join)(temp, `snippet-${index}.${block.language.toLowerCase() === "tsx" ? "tsx" : "ts"}`);
1839
+ const extension = block.language.toLowerCase() === "tsx" ? "tsx" : "ts";
1840
+ const file = (0, node_path.join)(temp, `snippet-${index}.${extension}`);
455
1841
  files.push(file);
456
1842
  await (0, node_fs_promises.writeFile)(file, block.code);
457
1843
  }));
@@ -463,7 +1849,7 @@ async function typecheckCodeBlocks(source, options = {}) {
463
1849
  ...files
464
1850
  ], {
465
1851
  cwd: process.cwd(),
466
- maxBuffer: 1024 * 1024 * 4
1852
+ maxBuffer: 4194304
467
1853
  });
468
1854
  return [];
469
1855
  } catch (error) {
@@ -561,7 +1947,7 @@ async function loadNapiBindings() {
561
1947
  if (napiLoadAttempted) return napiBindings ?? null;
562
1948
  napiLoadAttempted = true;
563
1949
  try {
564
- const mod = await require_napi.importNapiModule();
1950
+ const mod = await require_vitepress.importNapiModule();
565
1951
  napiBindings = mod;
566
1952
  return mod;
567
1953
  } catch (error) {
@@ -616,10 +2002,10 @@ async function transformMarkdown(source, filePath, options, ssgOptions) {
616
2002
  } : void 0
617
2003
  });
618
2004
  if (result.errors.length > 0) console.warn("[ox-content] Transform warnings:", result.errors);
619
- let html = result.html;
2005
+ let html = normalizeSelfClosingEmbeds(result.html);
620
2006
  const frontmatter = parseFrontmatterJson(result.frontmatter);
621
2007
  const toc = options.toc ? result.toc.map(normalizeTocEntry) : [];
622
- if (options.mermaid) html = await require_mermaid.transformMermaidStatic(html);
2008
+ if (options.mermaid) html = await transformMermaidStatic(html);
623
2009
  const { html: protectedHtml, svgs } = protectMermaidSvgs(html);
624
2010
  html = protectedHtml;
625
2011
  if (options.highlight) {
@@ -815,7 +2201,7 @@ const DEFAULT_DOCS_INCLUDE = [
815
2201
  * ```
816
2202
  */
817
2203
  async function extractDocs(srcDirs, options) {
818
- const napi = await require_napi.importNapiModule();
2204
+ const napi = await require_vitepress.importNapiModule();
819
2205
  if (options.entryPoints?.length) {
820
2206
  const extractDocsFromEntryPoints = napi.extractDocsFromEntryPoints;
821
2207
  if (!extractDocsFromEntryPoints) throw new Error("[ox-content] extractDocsFromEntryPoints is not available from @ox-content/napi.");
@@ -844,7 +2230,7 @@ async function extractDocs(srcDirs, options) {
844
2230
  * Generates Markdown documentation from extracted docs.
845
2231
  */
846
2232
  function generateMarkdown(docs, options) {
847
- const napi = require_napi.importNapiModuleSync();
2233
+ const napi = require_vitepress.importNapiModuleSync();
848
2234
  if (typeof napi.generateDocsMarkdown !== "function") throw new Error("[ox-content] generateDocsMarkdown is not available from @ox-content/napi. Please rebuild the NAPI package.");
849
2235
  return napi.generateDocsMarkdown(toRustDocsModules(docs), {
850
2236
  groupBy: options.groupBy,
@@ -874,7 +2260,7 @@ function generateMarkdown(docs, options) {
874
2260
  * Writes generated documentation to the output directory.
875
2261
  */
876
2262
  async function writeDocs(docs, outDir, extractedDocs, options) {
877
- const napi = require_napi.importNapiModuleSync();
2263
+ const napi = require_vitepress.importNapiModuleSync();
878
2264
  if (typeof napi.writeGeneratedDocs !== "function") throw new Error("[ox-content] writeGeneratedDocs is not available from @ox-content/napi. Please rebuild the NAPI package.");
879
2265
  napi.writeGeneratedDocs(docs, outDir, extractedDocs ? toRustDocsModules(extractedDocs) : void 0, {
880
2266
  generateNav: options?.generateNav ?? false,
@@ -1171,7 +2557,7 @@ function getDefaultTemplate() {
1171
2557
  const { title, description, siteName } = props;
1172
2558
  const rawBrand = siteName?.trim() ? siteName : "Ox Content";
1173
2559
  const isBrandCard = normalizeBrandValue(title) === normalizeBrandValue(rawBrand);
1174
- const heroTitle = isBrandCard ? "cargo doc for JavaScript" : title;
2560
+ const heroTitle = isBrandCard ? "High-performance Markdown toolkit" : title;
1175
2561
  const heroDescription = isBrandCard ? "Rust-powered docs and high-performance Markdown tooling." : description && description.trim().length > 0 ? description : "Rust-powered docs and Markdown tooling.";
1176
2562
  const descriptionHtml = heroDescription.trim().length > 0 ? `<p style="max-width:760px;font-size:28px;color:#93a4c3;line-height:1.45;margin:0;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;">${escapeHtml$2(heroDescription)}</p>` : "";
1177
2563
  return `<div style="width:100%;height:100%;position:relative;overflow:hidden;box-sizing:border-box;padding:56px 64px 52px;background:#0b1220;font-family:'IBM Plex Sans','Avenir Next','Segoe UI',system-ui,sans-serif;color:#eff6ff;border:1px solid #223252;border-top:4px solid #4f6fae;">
@@ -1226,7 +2612,7 @@ async function writeCache(cacheDir, key, png) {
1226
2612
  await fs_promises.writeFile(filePath, png);
1227
2613
  }
1228
2614
  //#endregion
1229
- //#region \0@oxc-project+runtime@0.129.0/helpers/usingCtx.js
2615
+ //#region \0@oxc-project+runtime@0.142.0/helpers/esm/usingCtx.js
1230
2616
  function _usingCtx() {
1231
2617
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
1232
2618
  var n = Error();
@@ -1393,7 +2779,8 @@ async function resolveVueTemplate(templatePath, options, root) {
1393
2779
  const { createSSRApp } = await import("vue");
1394
2780
  const { renderToString } = await import("vue/server-renderer");
1395
2781
  return async (props) => {
1396
- const html = await renderToString(createSSRApp(Component, props));
2782
+ const app = createSSRApp(Component, props);
2783
+ const html = await renderToString(app);
1397
2784
  if (extractedCss) return `<style>${extractedCss}</style>${html}`;
1398
2785
  return html;
1399
2786
  };
@@ -1908,13 +3295,13 @@ function resolveSsgOptions(ssg) {
1908
3295
  * Extracts title from content or frontmatter.
1909
3296
  */
1910
3297
  function extractTitle$1(content, frontmatter) {
1911
- return require_napi.importNapiModuleSync().extractSsgTitle(content, typeof frontmatter.title === "string" ? frontmatter.title : void 0);
3298
+ return require_vitepress.importNapiModuleSync().extractSsgTitle(content, typeof frontmatter.title === "string" ? frontmatter.title : void 0);
1912
3299
  }
1913
3300
  /**
1914
3301
  * Generates bare HTML page (no navigation, no styles).
1915
3302
  */
1916
3303
  function generateBareHtmlPage(content, title) {
1917
- return require_napi.importNapiModuleSync().generateSsgBareHtml(content, title);
3304
+ return require_vitepress.importNapiModuleSync().generateSsgBareHtml(content, title);
1918
3305
  }
1919
3306
  /**
1920
3307
  * Per-build cache for the Rust-facing nav conversion. `navGroups` is the same
@@ -1991,7 +3378,7 @@ function localeCodesFor(locales) {
1991
3378
  * Generates HTML page with navigation using Rust NAPI bindings.
1992
3379
  */
1993
3380
  async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
1994
- const mod = await require_napi.importNapiModule();
3381
+ const mod = await require_vitepress.importNapiModule();
1995
3382
  const tocForRust = pageData.toc.map(toRustTocEntry);
1996
3383
  const navGroupsForRust = convertNavGroupsForRust(navGroups);
1997
3384
  const themeForRust = theme ? require_vitepress.themeToNapi(theme) : void 0;
@@ -2044,7 +3431,7 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
2044
3431
  });
2045
3432
  }
2046
3433
  async function externalizeSharedPageAssets(pages, outDir, base) {
2047
- const optimized = (await require_napi.importNapiModule()).externalizeSsgAssets(pages, outDir, base);
3434
+ const optimized = (await require_vitepress.importNapiModule()).externalizeSsgAssets(pages, outDir, base);
2048
3435
  await Promise.all(optimized.assets.map(async (asset) => {
2049
3436
  await fs_promises.mkdir(path.dirname(asset.outputPath), { recursive: true });
2050
3437
  await fs_promises.writeFile(asset.outputPath, asset.content, "utf-8");
@@ -2058,45 +3445,45 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
2058
3445
  * Converts a markdown file path to a relative URL path.
2059
3446
  */
2060
3447
  function getUrlPath$1(inputPath, srcDir) {
2061
- return require_napi.importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
3448
+ return require_vitepress.importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
2062
3449
  }
2063
3450
  /**
2064
3451
  * Resolves manual navigation config to the format used by the built-in SSG renderer.
2065
3452
  */
2066
3453
  function resolveNavigationGroups(navigation, base, extension) {
2067
3454
  if (!navigation) return;
2068
- return require_napi.importNapiModuleSync().resolveSsgNavigationGroups(navigation, base, extension);
3455
+ return require_vitepress.importNapiModuleSync().resolveSsgNavigationGroups(navigation, base, extension);
2069
3456
  }
2070
3457
  function getPageLocale(urlPath, i18n) {
2071
3458
  if (!i18n) return void 0;
2072
- return require_napi.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, localeCodesFor(i18n.locales)) ?? void 0;
3459
+ return require_vitepress.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, localeCodesFor(i18n.locales)) ?? void 0;
2073
3460
  }
2074
3461
  function getRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl) {
2075
- return require_napi.importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
3462
+ return require_vitepress.importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
2076
3463
  }
2077
3464
  /**
2078
3465
  * Formats a file/dir name as a title.
2079
3466
  */
2080
3467
  function formatTitle(name) {
2081
- return require_napi.importNapiModuleSync().formatSsgTitle(name);
3468
+ return require_vitepress.importNapiModuleSync().formatSsgTitle(name);
2082
3469
  }
2083
3470
  /**
2084
3471
  * Collects all markdown files from the source directory.
2085
3472
  */
2086
3473
  async function collectMarkdownFiles(srcDir, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
2087
- return require_napi.importNapiModuleSync().collectSsgMarkdownFiles(srcDir, [...extensions]);
3474
+ return require_vitepress.importNapiModuleSync().collectSsgMarkdownFiles(srcDir, [...extensions]);
2088
3475
  }
2089
3476
  /**
2090
3477
  * Builds navigation items from markdown files, grouped by directory.
2091
3478
  */
2092
3479
  function buildNavItems(markdownFiles, srcDir, base, extension) {
2093
- return require_napi.importNapiModuleSync().buildSsgNavItems(markdownFiles, srcDir, base, extension);
3480
+ return require_vitepress.importNapiModuleSync().buildSsgNavItems(markdownFiles, srcDir, base, extension);
2094
3481
  }
2095
3482
  /**
2096
3483
  * Builds navigation items from an explicit theme sidebar tree.
2097
3484
  */
2098
3485
  function buildThemeNavItems(sidebar, base, extension) {
2099
- return require_napi.importNapiModuleSync().buildSsgThemeNavItems(sidebar, base, extension);
3486
+ return require_vitepress.importNapiModuleSync().buildSsgThemeNavItems(sidebar, base, extension);
2100
3487
  }
2101
3488
  /**
2102
3489
  * Builds all markdown files to static HTML.
@@ -2145,7 +3532,7 @@ async function createBuildSsgContext(options, root, srcDir, outDir, markdownFile
2145
3532
  navItems: resolveNavigationGroups(ssgOptions.navigation, base, ssgOptions.extension) ?? (ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension)),
2146
3533
  siteName: await resolveSiteName$1(root, ssgOptions),
2147
3534
  shouldGenerateOgImages: (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare,
2148
- napi: ssgOptions.lastUpdated ? await require_napi.importNapiModule() : void 0
3535
+ napi: ssgOptions.lastUpdated ? await require_vitepress.importNapiModule() : void 0
2149
3536
  };
2150
3537
  }
2151
3538
  async function resolveSiteName$1(root, ssgOptions) {
@@ -2321,7 +3708,7 @@ async function writeGeneratedPages(generatedPages, context, generatedFiles) {
2321
3708
  let oxContent$1 = null;
2322
3709
  async function getOxContent() {
2323
3710
  if (!oxContent$1) try {
2324
- oxContent$1 = await require_napi.importNapiModule();
3711
+ oxContent$1 = await require_vitepress.importNapiModule();
2325
3712
  } catch {
2326
3713
  console.warn("[ox-content] Native bindings not available, search disabled");
2327
3714
  return null;
@@ -2375,7 +3762,7 @@ async function writeSearchIndex(indexJson, outDir) {
2375
3762
  * This is injected into the bundle as a virtual module.
2376
3763
  */
2377
3764
  function generateSearchModule(options, indexPath) {
2378
- return require_napi.importNapiModuleSync().generateSearchModuleFromOptions(options, indexPath);
3765
+ return require_vitepress.importNapiModuleSync().generateSearchModuleFromOptions(options, indexPath);
2379
3766
  }
2380
3767
  //#endregion
2381
3768
  //#region src/dev-server.ts
@@ -2386,7 +3773,7 @@ function generateSearchModule(options, indexPath) {
2386
3773
  * during `vite dev`, matching the SSG build output.
2387
3774
  */
2388
3775
  /** File extensions to skip in the middleware. */
2389
- const SKIP_EXTENSIONS = new Set([
3776
+ const SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
2390
3777
  ".js",
2391
3778
  ".ts",
2392
3779
  ".css",
@@ -2995,7 +4382,7 @@ function createI18nPlugin(resolvedOptions) {
2995
4382
  return;
2996
4383
  }
2997
4384
  try {
2998
- const { checkI18nProject } = await require_napi.importNapiModule();
4385
+ const { checkI18nProject } = await require_vitepress.importNapiModule();
2999
4386
  const checkResult = checkI18nProject(dictDir, [path.resolve(root, "src"), path.resolve(root, "content")], i18nOptions.functionNames, i18nOptions.defaultLocale);
3000
4387
  if (checkResult.errorCount > 0 || checkResult.warningCount > 0) {
3001
4388
  for (const diag of checkResult.diagnostics) if (diag.severity === "error") console.error(`[ox-content:i18n] ${diag.message}`);
@@ -3288,7 +4675,7 @@ function resolveCollectionsOptions(options) {
3288
4675
  }
3289
4676
  async function buildCollectionManifest(root, options) {
3290
4677
  if (!options.collections.enabled) return { collections: {} };
3291
- return parseCollectionManifest((await require_napi.importNapiModule()).buildCollectionManifest({
4678
+ return parseCollectionManifest((await require_vitepress.importNapiModule()).buildCollectionManifest({
3292
4679
  srcDir: node_path.resolve(root, options.srcDir),
3293
4680
  extensions: [...options.extensions],
3294
4681
  frontmatter: options.frontmatter,
@@ -3386,7 +4773,7 @@ var IncrementalMarkdownParser = class {
3386
4773
  #includePendingAst;
3387
4774
  #completeInline;
3388
4775
  constructor(options = {}) {
3389
- const napi = require_napi.importNapiModuleSync();
4776
+ const napi = require_vitepress.importNapiModuleSync();
3390
4777
  this.#native = new napi.IncrementalMarkdownParser(toNativeParserOptions(options));
3391
4778
  this.#includePendingAst = options.includePendingAst ?? false;
3392
4779
  this.#completeInline = options.completeInline ?? true;
@@ -3422,7 +4809,7 @@ var IncrementalMarkdownRenderer = class {
3422
4809
  #renderPending;
3423
4810
  #completeInline;
3424
4811
  constructor(options = {}) {
3425
- const napi = require_napi.importNapiModuleSync();
4812
+ const napi = require_vitepress.importNapiModuleSync();
3426
4813
  this.#native = new napi.IncrementalMarkdownRenderer(toNativeParserOptions(options));
3427
4814
  this.#renderPending = options.renderPending ?? true;
3428
4815
  this.#completeInline = options.completeInline ?? true;
@@ -3575,7 +4962,7 @@ function renderHtmlToVueH(html, islands = []) {
3575
4962
  return renderHtmlToFrameworkCode(html, "vue", "expression", islands);
3576
4963
  }
3577
4964
  function renderHtmlToFrameworkCode(html, target, mode, islands = []) {
3578
- return require_napi.importNapiModuleSync().renderFrameworkComponentCode(html, target, toNapiIslands(islands), mode);
4965
+ return require_vitepress.importNapiModuleSync().renderFrameworkComponentCode(html, target, toNapiIslands(islands), mode);
3579
4966
  }
3580
4967
  function renderHtmlToReactComponent(html, islands = []) {
3581
4968
  return renderHtmlToFrameworkCode(html, "react", "component", islands);
@@ -3587,7 +4974,7 @@ function renderHtmlToSvelteComponent(html) {
3587
4974
  return renderHtmlToFrameworkCode(html, "svelte", "component");
3588
4975
  }
3589
4976
  function escapeSvelteMarkup(html) {
3590
- return require_napi.importNapiModuleSync().escapeSvelteMarkup(html);
4977
+ return require_vitepress.importNapiModuleSync().escapeSvelteMarkup(html);
3591
4978
  }
3592
4979
  function toNapiIslands(islands) {
3593
4980
  return islands.map((island) => ({
@@ -4237,7 +5624,7 @@ async function lintMarkdownFiles(options = {}) {
4237
5624
  function resolveMarkdownLintFileOptions(options) {
4238
5625
  return {
4239
5626
  cwd: node_path.resolve(options.cwd ?? process.cwd()),
4240
- exclude: [...new Set([...options.exclude ?? DEFAULT_LINT_FILE_EXCLUDE, ...options.ignore ?? []])],
5627
+ exclude: [.../* @__PURE__ */ new Set([...options.exclude ?? DEFAULT_LINT_FILE_EXCLUDE, ...options.ignore ?? []])],
4241
5628
  include: [...new Set(options.include ?? DEFAULT_LINT_FILE_INCLUDE)],
4242
5629
  lintOptions: {
4243
5630
  dictionary: options.dictionary,
@@ -4329,7 +5716,7 @@ function createEmptyLintResult() {
4329
5716
  * }
4330
5717
  * ```
4331
5718
  */
4332
- const VOID_ELEMENTS = new Set([
5719
+ const VOID_ELEMENTS = /* @__PURE__ */ new Set([
4333
5720
  "area",
4334
5721
  "base",
4335
5722
  "br",
@@ -4345,7 +5732,7 @@ const VOID_ELEMENTS = new Set([
4345
5732
  "track",
4346
5733
  "wbr"
4347
5734
  ]);
4348
- const BOOLEAN_ATTRS = new Set([
5735
+ const BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
4349
5736
  "allowfullscreen",
4350
5737
  "async",
4351
5738
  "autofocus",
@@ -4492,7 +5879,7 @@ function each(items, render) {
4492
5879
  }
4493
5880
  //#endregion
4494
5881
  //#region src/page-context.ts
4495
- var page_context_exports = /* @__PURE__ */ require_chunk.__exportAll({
5882
+ var page_context_exports = /* @__PURE__ */ require_vitepress.__exportAll({
4496
5883
  clearRenderContext: () => clearRenderContext,
4497
5884
  generateFrontmatterTypes: () => generateFrontmatterTypes,
4498
5885
  inferType: () => inferType,
@@ -4677,7 +6064,7 @@ function generateFrontmatterTypes(samples, interfaceName = "PageFrontmatter") {
4677
6064
  return lines.join("\n");
4678
6065
  }
4679
6066
  var currentContext;
4680
- var init_page_context = require_chunk.__esmMin((() => {
6067
+ var init_page_context = require_vitepress.__esmMin((() => {
4681
6068
  currentContext = null;
4682
6069
  }));
4683
6070
  //#endregion
@@ -4771,7 +6158,7 @@ async function generateTypes(pages, outDir) {
4771
6158
  * A minimal theme that renders page content with basic styling.
4772
6159
  */
4773
6160
  function DefaultTheme({ children }) {
4774
- const { usePageProps, useSiteConfig } = (init_page_context(), require_chunk.__toCommonJS(page_context_exports));
6161
+ const { usePageProps, useSiteConfig } = (init_page_context(), require_vitepress.__toCommonJS(page_context_exports));
4775
6162
  const page = usePageProps();
4776
6163
  const site = useSiteConfig();
4777
6164
  return { __html: `<!DOCTYPE html>
@@ -4835,7 +6222,7 @@ function escapeHtml(str) {
4835
6222
  function createTheme(config) {
4836
6223
  const { layouts, defaultLayout = "default" } = config;
4837
6224
  return function ThemeWithLayouts({ children }) {
4838
- const { usePageProps } = (init_page_context(), require_chunk.__toCommonJS(page_context_exports));
6225
+ const { usePageProps } = (init_page_context(), require_vitepress.__toCommonJS(page_context_exports));
4839
6226
  const layoutName = usePageProps().layout ?? defaultLayout;
4840
6227
  const Layout = layouts[layoutName] ?? layouts[defaultLayout];
4841
6228
  if (!Layout) throw new Error(`[ox-content] Layout "${layoutName}" not found. Available layouts: ${Object.keys(layouts).join(", ")}`);
@@ -5359,9 +6746,9 @@ function resolveCodeAnnotationsOptions(options) {
5359
6746
  /**
5360
6747
  * Generates virtual module content.
5361
6748
  */
5362
- function generateVirtualModule(path$1, options) {
5363
- if (path$1 === "config") return `export default ${JSON.stringify(options)};`;
5364
- if (path$1 === "runtime") {
6749
+ function generateVirtualModule(path$3, options) {
6750
+ if (path$3 === "config") return `export default ${JSON.stringify(options)};`;
6751
+ if (path$3 === "runtime") {
5365
6752
  const base = normalizeRuntimeBase(options.base);
5366
6753
  return `
5367
6754
  export const base = ${JSON.stringify(base)};
@@ -5420,9 +6807,9 @@ exports.buildSearchIndex = buildSearchIndex;
5420
6807
  exports.buildSsg = buildSsg;
5421
6808
  exports.clearRenderContext = clearRenderContext;
5422
6809
  exports.collectDocsTests = collectDocsTests;
5423
- exports.collectGitHubRepos = require_github.collectGitHubRepos;
5424
- exports.collectGitHubSources = require_github.collectGitHubSources;
5425
- exports.collectOgpUrls = require_ogp.collectOgpUrls;
6810
+ exports.collectGitHubRepos = collectGitHubRepos;
6811
+ exports.collectGitHubSources = collectGitHubSources;
6812
+ exports.collectOgpUrls = collectOgpUrls;
5426
6813
  exports.convertVitePressNav = require_vitepress.convertVitePressNav;
5427
6814
  exports.convertVitePressSidebar = require_vitepress.convertVitePressSidebar;
5428
6815
  exports.createFrameworkMarkdownOptions = createFrameworkMarkdownOptions;
@@ -5441,10 +6828,10 @@ exports.extractCodeBlocks = extractCodeBlocks;
5441
6828
  exports.extractDocs = extractDocs;
5442
6829
  exports.extractDocsTests = extractDocsTests;
5443
6830
  exports.extractIslandInfo = extractIslandInfo;
5444
- exports.extractVideoId = require_youtube.extractVideoId;
5445
- exports.fetchGitHubSource = require_github.fetchGitHubSource;
5446
- exports.fetchOgpData = require_ogp.fetchOgpData;
5447
- exports.fetchRepoData = require_github.fetchRepoData;
6831
+ exports.extractVideoId = extractVideoId;
6832
+ exports.fetchGitHubSource = fetchGitHubSource;
6833
+ exports.fetchOgpData = fetchOgpData;
6834
+ exports.fetchRepoData = fetchRepoData;
5448
6835
  exports.fromVitePressConfig = require_vitepress.fromVitePressConfig;
5449
6836
  exports.generateCollectionsVirtualModule = generateCollectionsVirtualModule;
5450
6837
  exports.generateFrontmatterTypes = generateFrontmatterTypes;
@@ -5466,15 +6853,15 @@ exports.lintMarkdownAsync = lintMarkdownAsync;
5466
6853
  exports.lintMarkdownFile = lintMarkdownFile;
5467
6854
  exports.lintMarkdownFiles = lintMarkdownFiles;
5468
6855
  exports.mergeThemes = require_vitepress.mergeThemes;
5469
- exports.mermaidClientScript = require_mermaid.mermaidClientScript;
6856
+ exports.mermaidClientScript = mermaidClientScript;
5470
6857
  exports.normalizeMarkdownExtensions = normalizeMarkdownExtensions;
5471
6858
  exports.normalizeVitePressFrontmatter = require_vitepress.normalizeVitePressFrontmatter;
5472
6859
  exports.oxContent = oxContent;
5473
- exports.parseGitHubLineRange = require_github.parseGitHubLineRange;
5474
- exports.parseGitHubPermalink = require_github.parseGitHubPermalink;
5475
- exports.prefetchGitHubRepos = require_github.prefetchGitHubRepos;
5476
- exports.prefetchGitHubSources = require_github.prefetchGitHubSources;
5477
- exports.prefetchOgpData = require_ogp.prefetchOgpData;
6860
+ exports.parseGitHubLineRange = parseGitHubLineRange;
6861
+ exports.parseGitHubPermalink = parseGitHubPermalink;
6862
+ exports.prefetchGitHubRepos = prefetchGitHubRepos;
6863
+ exports.prefetchGitHubSources = prefetchGitHubSources;
6864
+ exports.prefetchOgpData = prefetchOgpData;
5478
6865
  exports.raw = raw;
5479
6866
  exports.renderAllPages = renderAllPages;
5480
6867
  exports.renderHtmlToFrameworkCode = renderHtmlToFrameworkCode;
@@ -5499,13 +6886,13 @@ exports.setRenderContext = setRenderContext;
5499
6886
  exports.shouldLintMarkdownFile = shouldLintMarkdownFile;
5500
6887
  exports.stripMarkdownExtension = stripMarkdownExtension;
5501
6888
  exports.transformAllPlugins = transformAllPlugins;
5502
- exports.transformGitHub = require_github.transformGitHub;
6889
+ exports.transformGitHub = transformGitHub;
5503
6890
  exports.transformIslands = transformIslands;
5504
6891
  exports.transformMarkdown = transformMarkdown;
5505
- exports.transformMermaidStatic = require_mermaid.transformMermaidStatic;
5506
- exports.transformOgp = require_ogp.transformOgp;
6892
+ exports.transformMermaidStatic = transformMermaidStatic;
6893
+ exports.transformOgp = transformOgp;
5507
6894
  exports.transformTabs = require_tabs.transformTabs;
5508
- exports.transformYouTube = require_youtube.transformYouTube;
6895
+ exports.transformYouTube = transformYouTube;
5509
6896
  exports.typecheckCodeBlocks = typecheckCodeBlocks;
5510
6897
  exports.useIsActive = useIsActive;
5511
6898
  exports.useNav = useNav;