@meowdown/react 0.82.3 → 0.83.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { c } from "react/compiler-runtime";
2
2
  import { clsx } from "clsx/lite";
3
- import { Fragment, ViewTransition, cloneElement, createContext, createElement, memo, startTransition, use, useDeferredValue, useEffect, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from "react";
3
+ import { Fragment, ViewTransition, cloneElement, createContext, createElement, memo, startTransition, use, useDeferredValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useReducer, useRef, useState } from "react";
4
4
  import { buildFileMarkdown, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineCodeBlockPreviewPlugin, defineEditorExtension, defineLinkEditKeymap, defineLinkHoverHandler, definePendingReplacementHandler, defineSearchStatusHandler, defineVirtualCaret, defineWikilinkHoverHandler, dismissLinkHover, docToMarkdown, docToParagraphMarkdown, formatFileSize, getCodeTokens, getFileKind, getLinkText, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunksWithContext, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, normalizeHref, paragraphMarkdownToDoc, parsePostEmbedSnapshot, renderMathInto, updateEditorConfig } from "@meowdown/core";
5
5
  import { clamp } from "@ocavue/utils";
6
6
  import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, defineUpdateHandler, isTextSelection, union } from "@prosekit/core";
@@ -24,34 +24,30 @@ import { Mark } from "@prosekit/pm/model";
24
24
  import { BOOLEAN, OVERLOADED_BOOLEAN, getPropertyInfo, possibleStandardNames } from "react-property";
25
25
  import { PreviewCard } from "@base-ui/react/preview-card";
26
26
 
27
- // src/hooks/use-beautiful-mermaid.ts
28
- let beautifulMermaidPromise;
29
- function loadBeautifulMermaid() {
30
- beautifulMermaidPromise ??= import("./beautiful-mermaid-chunk-Cc6FHgAa.js").then((module) => module.renderMermaidSVG).catch((error) => {
31
- console.error("[meowdown] Failed to load beautiful-mermaid.", error);
32
- throw error;
33
- });
34
- return beautifulMermaidPromise;
35
- }
36
- function useBeautifulMermaid(enabled) {
27
+ // src/hooks/use-katex.ts
28
+ /**
29
+ * The lazily loaded KaTeX render function, or `undefined` while it loads (or when
30
+ * `enabled` is false, so a document without math never loads it).
31
+ */
32
+ function useKaTeX(enabled) {
37
33
  const $ = c(4);
38
- const [renderer, setRenderer] = useState(void 0);
34
+ const [katex, setKaTeX] = useState(void 0);
39
35
  let t0;
40
36
  let t1;
41
- if ($[0] !== enabled || $[1] !== renderer) {
37
+ if ($[0] !== enabled || $[1] !== katex) {
42
38
  t0 = () => {
43
- if (!enabled || renderer) return;
39
+ if (!enabled || katex) return;
44
40
  let cancelled = false;
45
- loadBeautifulMermaid().then((loadedRenderer) => {
46
- if (!cancelled) setRenderer(() => loadedRenderer);
41
+ loadKaTeX().then((render) => {
42
+ if (!cancelled) setKaTeX(() => render);
47
43
  });
48
44
  return () => {
49
45
  cancelled = true;
50
46
  };
51
47
  };
52
- t1 = [enabled, renderer];
48
+ t1 = [enabled, katex];
53
49
  $[0] = enabled;
54
- $[1] = renderer;
50
+ $[1] = katex;
55
51
  $[2] = t0;
56
52
  $[3] = t1;
57
53
  } else {
@@ -59,33 +55,37 @@ function useBeautifulMermaid(enabled) {
59
55
  t1 = $[3];
60
56
  }
61
57
  useEffect(t0, t1);
62
- return renderer;
58
+ return katex;
63
59
  }
64
60
 
65
- // src/hooks/use-katex.ts
66
- /**
67
- * The lazily loaded KaTeX render function, or `undefined` while it loads (or when
68
- * `enabled` is false, so a document without math never loads it).
69
- */
70
- function useKaTeX(enabled) {
61
+ // src/hooks/use-pele.ts
62
+ let pelePromise;
63
+ function loadPele() {
64
+ pelePromise ??= import("./pele-chunk-Sqn5D2fk.js").then((module) => module.renderMermaid).catch((error) => {
65
+ console.error("[meowdown] Failed to load pele.", error);
66
+ throw error;
67
+ });
68
+ return pelePromise;
69
+ }
70
+ function usePele(enabled) {
71
71
  const $ = c(4);
72
- const [katex, setKaTeX] = useState(void 0);
72
+ const [renderer, setRenderer] = useState(void 0);
73
73
  let t0;
74
74
  let t1;
75
- if ($[0] !== enabled || $[1] !== katex) {
75
+ if ($[0] !== enabled || $[1] !== renderer) {
76
76
  t0 = () => {
77
- if (!enabled || katex) return;
77
+ if (!enabled || renderer) return;
78
78
  let cancelled = false;
79
- loadKaTeX().then((render) => {
80
- if (!cancelled) setKaTeX(() => render);
79
+ loadPele().then((loadedRenderer) => {
80
+ if (!cancelled) setRenderer(() => loadedRenderer);
81
81
  });
82
82
  return () => {
83
83
  cancelled = true;
84
84
  };
85
85
  };
86
- t1 = [enabled, katex];
86
+ t1 = [enabled, renderer];
87
87
  $[0] = enabled;
88
- $[1] = katex;
88
+ $[1] = renderer;
89
89
  $[2] = t0;
90
90
  $[3] = t1;
91
91
  } else {
@@ -93,7 +93,7 @@ function useKaTeX(enabled) {
93
93
  t1 = $[3];
94
94
  }
95
95
  useEffect(t0, t1);
96
- return katex;
96
+ return renderer;
97
97
  }
98
98
 
99
99
  // src/utils/non-prose-props.ts
@@ -278,99 +278,107 @@ function MathRender(props) {
278
278
  }
279
279
 
280
280
  // src/components/mermaid-render.tsx
281
- const MERMAID_OPTIONS = {
282
- bg: "var(--meowdown-mermaid-bg)",
283
- fg: "var(--meowdown-mermaid-fg)",
284
- line: "var(--meowdown-mermaid-line)",
285
- accent: "var(--meowdown-mermaid-accent)",
286
- muted: "var(--meowdown-mermaid-muted)",
287
- surface: "var(--meowdown-mermaid-surface)",
288
- border: "var(--meowdown-mermaid-border)",
289
- transparent: true,
290
- interactive: false
281
+ const PELE_OPTIONS = {
282
+ links: false,
283
+ images: false,
284
+ responsive: false,
285
+ autoDirection: false
291
286
  };
292
- function renderMermaid(renderer, source) {
287
+ function getMermaidOutput(renderer, source, idPrefix) {
293
288
  try {
294
- const svg = renderer(source, MERMAID_OPTIONS);
295
- const document = new DOMParser().parseFromString(svg, "image/svg+xml");
296
- const element = document.documentElement;
297
- if (document.querySelector("parsererror") || element.localName !== "svg" || element.namespaceURI !== "http://www.w3.org/2000/svg") return { error: "Invalid SVG output." };
298
- return { element };
289
+ return { element: renderer(source, {
290
+ ...PELE_OPTIONS,
291
+ idPrefix
292
+ }) };
299
293
  } catch (error) {
300
294
  return { error: error instanceof Error ? error.message : String(error) };
301
295
  }
302
296
  }
303
297
  function MermaidRender(props) {
304
- const $ = c(18);
298
+ const $ = c(22);
305
299
  const { renderer, source, className, onMouseDown } = props;
306
- let t0;
307
- if ($[0] !== renderer || $[1] !== source) {
308
- t0 = renderMermaid(renderer, source);
309
- $[0] = renderer;
310
- $[1] = source;
311
- $[2] = t0;
312
- } else t0 = $[2];
313
- const output = t0;
314
- const ref = useRef(null);
300
+ const t0 = useId();
315
301
  let t1;
316
- if ($[3] !== output.element) {
317
- t1 = () => {
302
+ if ($[0] !== t0) {
303
+ let t2;
304
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
305
+ t2 = /[^\w-]/g;
306
+ $[2] = t2;
307
+ } else t2 = $[2];
308
+ t1 = t0.replaceAll(t2, "");
309
+ $[0] = t0;
310
+ $[1] = t1;
311
+ } else t1 = $[1];
312
+ const idPrefix = `meowdown-mermaid-${t1}`;
313
+ let t2;
314
+ if ($[3] !== idPrefix || $[4] !== renderer || $[5] !== source) {
315
+ t2 = getMermaidOutput(renderer, source, idPrefix);
316
+ $[3] = idPrefix;
317
+ $[4] = renderer;
318
+ $[5] = source;
319
+ $[6] = t2;
320
+ } else t2 = $[6];
321
+ const output = t2;
322
+ const ref = useRef(null);
323
+ let t3;
324
+ if ($[7] !== output.element) {
325
+ t3 = () => {
318
326
  const host = ref.current;
319
327
  if (!host || !output.element) return;
320
328
  host.replaceChildren(window.document.importNode(output.element, true));
321
329
  };
322
- $[3] = output.element;
323
- $[4] = t1;
324
- } else t1 = $[4];
325
- let t2;
326
- if ($[5] !== output) {
327
- t2 = [output];
328
- $[5] = output;
329
- $[6] = t2;
330
- } else t2 = $[6];
331
- useLayoutEffect(t1, t2);
330
+ $[7] = output.element;
331
+ $[8] = t3;
332
+ } else t3 = $[8];
333
+ let t4;
334
+ if ($[9] !== output) {
335
+ t4 = [output];
336
+ $[9] = output;
337
+ $[10] = t4;
338
+ } else t4 = $[10];
339
+ useLayoutEffect(t3, t4);
332
340
  if (output.error) {
333
- const t3 = props["data-testid"];
334
- const t4 = props["data-scroll-x"];
335
- let t5;
336
- if ($[7] !== className || $[8] !== onMouseDown || $[9] !== output.error || $[10] !== t3 || $[11] !== t4) {
337
- t5 = /* @__PURE__ */ jsx("span", {
341
+ const t5 = props["data-testid"];
342
+ const t6 = props["data-scroll-x"];
343
+ let t7;
344
+ if ($[11] !== className || $[12] !== onMouseDown || $[13] !== output.error || $[14] !== t5 || $[15] !== t6) {
345
+ t7 = /* @__PURE__ */ jsx("span", {
338
346
  className,
339
347
  contentEditable: false,
340
348
  "data-error": true,
341
- "data-testid": t3,
342
- "data-scroll-x": t4,
349
+ "data-testid": t5,
350
+ "data-scroll-x": t6,
343
351
  onMouseDown,
344
352
  children: output.error
345
353
  }, "error");
346
- $[7] = className;
347
- $[8] = onMouseDown;
348
- $[9] = output.error;
349
- $[10] = t3;
350
- $[11] = t4;
351
- $[12] = t5;
352
- } else t5 = $[12];
353
- return t5;
354
+ $[11] = className;
355
+ $[12] = onMouseDown;
356
+ $[13] = output.error;
357
+ $[14] = t5;
358
+ $[15] = t6;
359
+ $[16] = t7;
360
+ } else t7 = $[16];
361
+ return t7;
354
362
  }
355
- const t3 = props["data-testid"];
356
- const t4 = props["data-scroll-x"];
357
- let t5;
358
- if ($[13] !== className || $[14] !== onMouseDown || $[15] !== t3 || $[16] !== t4) {
359
- t5 = /* @__PURE__ */ jsx("span", {
363
+ const t5 = props["data-testid"];
364
+ const t6 = props["data-scroll-x"];
365
+ let t7;
366
+ if ($[17] !== className || $[18] !== onMouseDown || $[19] !== t5 || $[20] !== t6) {
367
+ t7 = /* @__PURE__ */ jsx("span", {
360
368
  ref,
361
369
  className,
362
370
  contentEditable: false,
363
- "data-testid": t3,
364
- "data-scroll-x": t4,
371
+ "data-testid": t5,
372
+ "data-scroll-x": t6,
365
373
  onMouseDown
366
374
  }, "svg");
367
- $[13] = className;
368
- $[14] = onMouseDown;
369
- $[15] = t3;
370
- $[16] = t4;
371
- $[17] = t5;
372
- } else t5 = $[17];
373
- return t5;
375
+ $[17] = className;
376
+ $[18] = onMouseDown;
377
+ $[19] = t5;
378
+ $[20] = t6;
379
+ $[21] = t7;
380
+ } else t7 = $[21];
381
+ return t7;
374
382
  }
375
383
 
376
384
  // src/components/code-block-view.tsx
@@ -389,7 +397,7 @@ function CodeBlockView(props) {
389
397
  } else t0 = $[1];
390
398
  const caretInside = t0;
391
399
  const katex = useKaTeX(isMath);
392
- const mermaid = useBeautifulMermaid(isMermaid);
400
+ const mermaid = usePele(isMermaid);
393
401
  const showMathPreview = isMath && katex != null;
394
402
  const showMermaidPreview = isMermaid && mermaid != null;
395
403
  const showPreview = showMathPreview || showMermaidPreview;
@@ -5322,7 +5330,7 @@ function MathCodeBlock(t0) {
5322
5330
  function MermaidCodeBlock(t0) {
5323
5331
  const $ = c(5);
5324
5332
  const { code } = t0;
5325
- const renderer = useBeautifulMermaid(true);
5333
+ const renderer = usePele(true);
5326
5334
  if (!renderer || code.trim() === "") {
5327
5335
  let t1;
5328
5336
  if ($[0] !== code) {
@@ -0,0 +1,30 @@
1
+ import { render } from "pele";
2
+ import DOMPurify from "dompurify";
3
+
4
+ // src/utils/sanitize-svg.ts
5
+ /**
6
+ * Parses an SVG string and removes anything that can run script. Returns
7
+ * `undefined` when no single `<svg>` root remains.
8
+ */
9
+ function sanitizeSvg(svg) {
10
+ const fragment = DOMPurify.sanitize(svg, {
11
+ USE_PROFILES: {
12
+ svg: true,
13
+ svgFilters: true
14
+ },
15
+ ADD_ATTR: ["role"],
16
+ RETURN_DOM_FRAGMENT: true
17
+ });
18
+ const element = fragment.firstElementChild;
19
+ if (fragment.childElementCount !== 1 || element?.localName !== "svg") return;
20
+ return element;
21
+ }
22
+
23
+ // src/hooks/pele-chunk.ts
24
+ function renderMermaid(source, options) {
25
+ const element = sanitizeSvg(render(source, options).svg);
26
+ if (!element) throw new Error("Invalid SVG output.");
27
+ return element;
28
+ }
29
+
30
+ export { renderMermaid };
package/dist/style.css CHANGED
@@ -27,6 +27,15 @@
27
27
  }
28
28
 
29
29
  .meow_MermaidPreview_D3j_9q {
30
+ --pele-bg: var(--meowdown-mermaid-bg);
31
+ --pele-fg: var(--meowdown-mermaid-fg);
32
+ --pele-muted: var(--meowdown-mermaid-muted);
33
+ --pele-line: var(--meowdown-mermaid-line);
34
+ --pele-surface: var(--meowdown-mermaid-surface);
35
+ --pele-surface-alt: var(--meowdown-mermaid-surface-alt);
36
+ --pele-border: var(--meowdown-mermaid-border);
37
+ --pele-accent: var(--meowdown-mermaid-accent);
38
+ --pele-critical: var(--meowdown-mermaid-critical);
30
39
  text-align: initial;
31
40
 
32
41
  &[data-error] {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@meowdown/react",
3
3
  "type": "module",
4
- "version": "0.82.3",
4
+ "version": "0.83.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -25,16 +25,17 @@
25
25
  "@meowdown/core": "0.83.0",
26
26
  "@meowdown/embed": "0.3.1",
27
27
  "@meowdown/markdown": "0.78.0",
28
- "@ocavue/utils": "^1.9.0",
28
+ "@ocavue/utils": "^1.11.0",
29
29
  "@post-embed/types": "^0.5.0",
30
30
  "@prosekit/core": "^0.13.4",
31
31
  "@prosekit/pm": "^0.1.21",
32
32
  "@prosekit/react": "^0.9.0",
33
33
  "@prosekit/web": "^0.10.0",
34
- "beautiful-mermaid": "^1.1.3",
35
34
  "clsx": "^2.1.1",
35
+ "dompurify": "^3.4.17",
36
36
  "github-slugger": "^2.0.0",
37
37
  "lucide-react": "^1.47.0",
38
+ "pele": "^1.13.0",
38
39
  "react-property": "^2.0.2"
39
40
  },
40
41
  "peerDependencies": {
@@ -50,7 +51,7 @@
50
51
  }
51
52
  },
52
53
  "devDependencies": {
53
- "@css-modules-kit/codegen": "^1.4.0",
54
+ "@css-modules-kit/codegen": "^1.4.1",
54
55
  "@meowdown/vitest": "0.0.0",
55
56
  "@ocavue/tsconfig": "^0.7.1",
56
57
  "@tsdown/css": "^0.23.3",
@@ -1,3 +0,0 @@
1
- import { renderMermaidSVG } from "beautiful-mermaid";
2
-
3
- export { renderMermaidSVG };