@meowdown/react 0.82.2 → 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
@@ -230,7 +230,7 @@ function _temp$4(event) {
230
230
  * mode emits a block-level `math[display="block"]` element.
231
231
  */
232
232
  function MathRender(props) {
233
- const $ = c(9);
233
+ const $ = c(10);
234
234
  const { katex, formula, displayMode, className, onMouseDown } = props;
235
235
  const ref = useRef(null);
236
236
  let t0;
@@ -257,111 +257,128 @@ function MathRender(props) {
257
257
  }
258
258
  useLayoutEffect(t0, t1);
259
259
  const t2 = props["data-testid"];
260
- let t3;
261
- if ($[5] !== className || $[6] !== onMouseDown || $[7] !== t2) {
262
- t3 = /* @__PURE__ */ jsx("span", {
260
+ const t3 = props["data-scroll-x"];
261
+ let t4;
262
+ if ($[5] !== className || $[6] !== onMouseDown || $[7] !== t2 || $[8] !== t3) {
263
+ t4 = /* @__PURE__ */ jsx("span", {
263
264
  ref,
264
265
  className,
265
266
  contentEditable: false,
266
267
  "data-testid": t2,
268
+ "data-scroll-x": t3,
267
269
  onMouseDown
268
270
  });
269
271
  $[5] = className;
270
272
  $[6] = onMouseDown;
271
273
  $[7] = t2;
272
274
  $[8] = t3;
273
- } else t3 = $[8];
274
- return t3;
275
+ $[9] = t4;
276
+ } else t4 = $[9];
277
+ return t4;
275
278
  }
276
279
 
277
280
  // src/components/mermaid-render.tsx
278
- const MERMAID_OPTIONS = {
279
- bg: "var(--meowdown-mermaid-bg)",
280
- fg: "var(--meowdown-mermaid-fg)",
281
- line: "var(--meowdown-mermaid-line)",
282
- accent: "var(--meowdown-mermaid-accent)",
283
- muted: "var(--meowdown-mermaid-muted)",
284
- surface: "var(--meowdown-mermaid-surface)",
285
- border: "var(--meowdown-mermaid-border)",
286
- transparent: true,
287
- interactive: false
281
+ const PELE_OPTIONS = {
282
+ links: false,
283
+ images: false,
284
+ responsive: false,
285
+ autoDirection: false
288
286
  };
289
- function renderMermaid(renderer, source) {
287
+ function getMermaidOutput(renderer, source, idPrefix) {
290
288
  try {
291
- const svg = renderer(source, MERMAID_OPTIONS);
292
- const document = new DOMParser().parseFromString(svg, "image/svg+xml");
293
- const element = document.documentElement;
294
- if (document.querySelector("parsererror") || element.localName !== "svg" || element.namespaceURI !== "http://www.w3.org/2000/svg") return { error: "Invalid SVG output." };
295
- return { element };
289
+ return { element: renderer(source, {
290
+ ...PELE_OPTIONS,
291
+ idPrefix
292
+ }) };
296
293
  } catch (error) {
297
294
  return { error: error instanceof Error ? error.message : String(error) };
298
295
  }
299
296
  }
300
297
  function MermaidRender(props) {
301
- const $ = c(16);
298
+ const $ = c(22);
302
299
  const { renderer, source, className, onMouseDown } = props;
303
- let t0;
304
- if ($[0] !== renderer || $[1] !== source) {
305
- t0 = renderMermaid(renderer, source);
306
- $[0] = renderer;
307
- $[1] = source;
308
- $[2] = t0;
309
- } else t0 = $[2];
310
- const output = t0;
311
- const ref = useRef(null);
300
+ const t0 = useId();
312
301
  let t1;
313
- if ($[3] !== output.element) {
314
- 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 = () => {
315
326
  const host = ref.current;
316
327
  if (!host || !output.element) return;
317
328
  host.replaceChildren(window.document.importNode(output.element, true));
318
329
  };
319
- $[3] = output.element;
320
- $[4] = t1;
321
- } else t1 = $[4];
322
- let t2;
323
- if ($[5] !== output) {
324
- t2 = [output];
325
- $[5] = output;
326
- $[6] = t2;
327
- } else t2 = $[6];
328
- 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);
329
340
  if (output.error) {
330
- const t3 = props["data-testid"];
331
- let t4;
332
- if ($[7] !== className || $[8] !== onMouseDown || $[9] !== output.error || $[10] !== t3) {
333
- t4 = /* @__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", {
334
346
  className,
335
347
  contentEditable: false,
336
348
  "data-error": true,
337
- "data-testid": t3,
349
+ "data-testid": t5,
350
+ "data-scroll-x": t6,
338
351
  onMouseDown,
339
352
  children: output.error
340
353
  }, "error");
341
- $[7] = className;
342
- $[8] = onMouseDown;
343
- $[9] = output.error;
344
- $[10] = t3;
345
- $[11] = t4;
346
- } else t4 = $[11];
347
- return t4;
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;
348
362
  }
349
- const t3 = props["data-testid"];
350
- let t4;
351
- if ($[12] !== className || $[13] !== onMouseDown || $[14] !== t3) {
352
- t4 = /* @__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", {
353
368
  ref,
354
369
  className,
355
370
  contentEditable: false,
356
- "data-testid": t3,
371
+ "data-testid": t5,
372
+ "data-scroll-x": t6,
357
373
  onMouseDown
358
374
  }, "svg");
359
- $[12] = className;
360
- $[13] = onMouseDown;
361
- $[14] = t3;
362
- $[15] = t4;
363
- } else t4 = $[15];
364
- return t4;
375
+ $[17] = className;
376
+ $[18] = onMouseDown;
377
+ $[19] = t5;
378
+ $[20] = t6;
379
+ $[21] = t7;
380
+ } else t7 = $[21];
381
+ return t7;
365
382
  }
366
383
 
367
384
  // src/components/code-block-view.tsx
@@ -380,7 +397,7 @@ function CodeBlockView(props) {
380
397
  } else t0 = $[1];
381
398
  const caretInside = t0;
382
399
  const katex = useKaTeX(isMath);
383
- const mermaid = useBeautifulMermaid(isMermaid);
400
+ const mermaid = usePele(isMermaid);
384
401
  const showMathPreview = isMath && katex != null;
385
402
  const showMermaidPreview = isMermaid && mermaid != null;
386
403
  const showPreview = showMathPreview || showMermaidPreview;
@@ -425,6 +442,7 @@ function CodeBlockView(props) {
425
442
  t6 = /* @__PURE__ */ jsx("pre", {
426
443
  ref: contentRef,
427
444
  "data-language": language,
445
+ "data-scroll-x": true,
428
446
  ...NON_PROSE_PROPS
429
447
  });
430
448
  $[11] = contentRef;
@@ -452,6 +470,7 @@ function CodeBlockView(props) {
452
470
  displayMode: true,
453
471
  className: code_block_view_module_default.Preview,
454
472
  "data-testid": "code-block-math-preview",
473
+ "data-scroll-x": true,
455
474
  onMouseDown: focusSource
456
475
  });
457
476
  $[19] = focusSource;
@@ -467,6 +486,7 @@ function CodeBlockView(props) {
467
486
  source: previewCode,
468
487
  className: `${code_block_view_module_default.Preview} ${code_block_view_module_default.MermaidPreview}`,
469
488
  "data-testid": "code-block-mermaid-preview",
489
+ "data-scroll-x": true,
470
490
  onMouseDown: focusSource
471
491
  });
472
492
  $[24] = focusSource;
@@ -5211,6 +5231,7 @@ function CodeBlock(t0) {
5211
5231
  t5 = /* @__PURE__ */ jsx("pre", {
5212
5232
  "data-code-block": "",
5213
5233
  "data-language": t2,
5234
+ "data-scroll-x": true,
5214
5235
  children: t4
5215
5236
  });
5216
5237
  $[8] = t2;
@@ -5297,7 +5318,8 @@ function MathCodeBlock(t0) {
5297
5318
  formula: code,
5298
5319
  displayMode: true,
5299
5320
  className: code_block_view_module_default.Preview,
5300
- "data-testid": "code-block-math-preview"
5321
+ "data-testid": "code-block-math-preview",
5322
+ "data-scroll-x": true
5301
5323
  });
5302
5324
  $[2] = code;
5303
5325
  $[3] = katex;
@@ -5308,7 +5330,7 @@ function MathCodeBlock(t0) {
5308
5330
  function MermaidCodeBlock(t0) {
5309
5331
  const $ = c(5);
5310
5332
  const { code } = t0;
5311
- const renderer = useBeautifulMermaid(true);
5333
+ const renderer = usePele(true);
5312
5334
  if (!renderer || code.trim() === "") {
5313
5335
  let t1;
5314
5336
  if ($[0] !== code) {
@@ -5327,7 +5349,8 @@ function MermaidCodeBlock(t0) {
5327
5349
  renderer,
5328
5350
  source: code,
5329
5351
  className: `${code_block_view_module_default.Preview} ${code_block_view_module_default.MermaidPreview}`,
5330
- "data-testid": "code-block-mermaid-preview"
5352
+ "data-testid": "code-block-mermaid-preview",
5353
+ "data-scroll-x": true
5331
5354
  });
5332
5355
  $[2] = code;
5333
5356
  $[3] = renderer;
@@ -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.2",
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 };