@memberjunction/ng-markdown 6.2.0-edge.2 → 6.2.0-edge.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/lib/components/markdown.component.d.ts +6 -1
- package/dist/lib/components/markdown.component.d.ts.map +1 -1
- package/dist/lib/components/markdown.component.dom.test.js +40 -1
- package/dist/lib/components/markdown.component.dom.test.js.map +1 -1
- package/dist/lib/components/markdown.component.js +24 -4
- package/dist/lib/components/markdown.component.js.map +1 -1
- package/dist/lib/services/markdown.service.d.ts +38 -0
- package/dist/lib/services/markdown.service.d.ts.map +1 -1
- package/dist/lib/services/markdown.service.dom.test.js +149 -0
- package/dist/lib/services/markdown.service.dom.test.js.map +1 -1
- package/dist/lib/services/markdown.service.js +180 -4
- package/dist/lib/services/markdown.service.js.map +1 -1
- package/package.json +4 -3
|
@@ -36,8 +36,10 @@ import * as i0 from "@angular/core";
|
|
|
36
36
|
* into other packages' DOMPurify usage, and so the configuration is parsed once instead
|
|
37
37
|
* of on every render.
|
|
38
38
|
*
|
|
39
|
-
* Profile: HTML + SVG + SVG filters, which keeps structural markup, inline styles, data
|
|
40
|
-
* attributes
|
|
39
|
+
* Profile: HTML + SVG + SVG filters + MathML, which keeps structural markup, inline styles, data
|
|
40
|
+
* attributes, inline vector graphics and formulas written as MathML, and removes the script vectors.
|
|
41
|
+
* MathML is DOMPurify's own profile, with its namespace checks (the mXSS class of bypasses through
|
|
42
|
+
* `<math>` is what those checks exist for). Deviations from
|
|
41
43
|
* DOMPurify's defaults, each deliberate:
|
|
42
44
|
* - FORCE_BODY: a document that starts with `<style>` keeps it (otherwise the parser hoists
|
|
43
45
|
* a leading style element into <head> and it is dropped); agent-authored mockups start
|
|
@@ -47,6 +49,13 @@ import * as i0 from "@angular/core";
|
|
|
47
49
|
* heading ids produce. This app does not read globals off `window` by element id, so
|
|
48
50
|
* DOM clobbering has no effect here, and heading anchors matter.
|
|
49
51
|
* - `target` on links is kept, with `rel="noopener noreferrer"` enforced when it is set.
|
|
52
|
+
* - `columnalign` and `columnspacing` are added: DOMPurify's MathML list spells the first
|
|
53
|
+
* `columnsalign` and lacks the second, so MathML tables lost their layout in Firefox and Safari
|
|
54
|
+
* (Chrome ignores both; the component stylesheet aligns columns there). Both are presentational. (`form` on `<mo>` stays out: on HTML it associates an element with a form, and
|
|
55
|
+
* MathML infers an operator's form without it.)
|
|
56
|
+
* - `annotation` / `annotation-xml` content is dropped with the element. DOMPurify removes both but
|
|
57
|
+
* would keep their text, and MathJax, Pandoc and Word MathML carry the TeX source there, so a
|
|
58
|
+
* formula would read "x2x^2".
|
|
50
59
|
* - `<use>` is allowed only with a same-document reference (`#id`); external references
|
|
51
60
|
* are removed. SMIL `<animate>`/`<set>` stay disallowed: they can rewrite `href`.
|
|
52
61
|
*/
|
|
@@ -60,9 +69,10 @@ function getPurifier() {
|
|
|
60
69
|
if (!instance.isSupported)
|
|
61
70
|
return null;
|
|
62
71
|
instance.setConfig({
|
|
63
|
-
USE_PROFILES: { html: true, svg: true, svgFilters: true },
|
|
72
|
+
USE_PROFILES: { html: true, svg: true, svgFilters: true, mathMl: true },
|
|
64
73
|
ADD_TAGS: ['use'],
|
|
65
|
-
ADD_ATTR: ['target'],
|
|
74
|
+
ADD_ATTR: ['target', 'columnalign', 'columnspacing'],
|
|
75
|
+
ADD_FORBID_CONTENTS: ['annotation', 'annotation-xml'],
|
|
66
76
|
FORCE_BODY: true,
|
|
67
77
|
SANITIZE_DOM: false,
|
|
68
78
|
});
|
|
@@ -90,6 +100,75 @@ function getPurifier() {
|
|
|
90
100
|
purifier = instance;
|
|
91
101
|
return purifier;
|
|
92
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Unicode's Mathematical Alphanumeric Symbols by MathML `mathvariant`: the code points of capital
|
|
105
|
+
* A, small a and digit 0 (0 where Unicode has no digits in that style).
|
|
106
|
+
*/
|
|
107
|
+
const MATH_LETTER_BASES = {
|
|
108
|
+
bold: [0x1d400, 0x1d41a, 0x1d7ce],
|
|
109
|
+
italic: [0x1d434, 0x1d44e, 0],
|
|
110
|
+
'bold-italic': [0x1d468, 0x1d482, 0],
|
|
111
|
+
script: [0x1d49c, 0x1d4b6, 0],
|
|
112
|
+
'bold-script': [0x1d4d0, 0x1d4ea, 0],
|
|
113
|
+
fraktur: [0x1d504, 0x1d51e, 0],
|
|
114
|
+
'double-struck': [0x1d538, 0x1d552, 0x1d7d8],
|
|
115
|
+
'bold-fraktur': [0x1d56c, 0x1d586, 0],
|
|
116
|
+
'sans-serif': [0x1d5a0, 0x1d5ba, 0x1d7e2],
|
|
117
|
+
'bold-sans-serif': [0x1d5d4, 0x1d5ee, 0x1d7ec],
|
|
118
|
+
'sans-serif-italic': [0x1d608, 0x1d622, 0],
|
|
119
|
+
'sans-serif-bold-italic': [0x1d63c, 0x1d656, 0],
|
|
120
|
+
monospace: [0x1d670, 0x1d68a, 0x1d7f6],
|
|
121
|
+
};
|
|
122
|
+
/** Letters Unicode encoded (in Letterlike Symbols) before that block existed; it has holes where they would go. */
|
|
123
|
+
const MATH_LETTER_HOLES = {
|
|
124
|
+
'italic h': 'ℎ',
|
|
125
|
+
'script B': 'ℬ', 'script E': 'ℰ', 'script F': 'ℱ', 'script H': 'ℋ', 'script I': 'ℐ', 'script L': 'ℒ',
|
|
126
|
+
'script M': 'ℳ', 'script R': 'ℛ', 'script e': 'ℯ', 'script g': 'ℊ', 'script o': 'ℴ',
|
|
127
|
+
'fraktur C': 'ℭ', 'fraktur H': 'ℌ', 'fraktur I': 'ℑ', 'fraktur R': 'ℜ', 'fraktur Z': 'ℨ',
|
|
128
|
+
'double-struck C': 'ℂ', 'double-struck H': 'ℍ', 'double-struck N': 'ℕ', 'double-struck P': 'ℙ',
|
|
129
|
+
'double-struck Q': 'ℚ', 'double-struck R': 'ℝ', 'double-struck Z': 'ℤ',
|
|
130
|
+
};
|
|
131
|
+
function toMathLetter(char, variant) {
|
|
132
|
+
const hole = MATH_LETTER_HOLES[`${variant} ${char}`];
|
|
133
|
+
if (hole)
|
|
134
|
+
return hole;
|
|
135
|
+
const bases = MATH_LETTER_BASES[variant];
|
|
136
|
+
if (!bases)
|
|
137
|
+
return null;
|
|
138
|
+
const code = char.charCodeAt(0);
|
|
139
|
+
if (code >= 65 && code <= 90)
|
|
140
|
+
return String.fromCodePoint(bases[0] + code - 65);
|
|
141
|
+
if (code >= 97 && code <= 122)
|
|
142
|
+
return String.fromCodePoint(bases[1] + code - 97);
|
|
143
|
+
if (code >= 48 && code <= 57 && bases[2])
|
|
144
|
+
return String.fromCodePoint(bases[2] + code - 48);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Chrome lays out MathML Core, which honors `mathvariant` only as "normal". KaTeX writes
|
|
149
|
+
* \mathbf{E} as `<mi mathvariant="bold">E</mi>` and \mathbb{R} as `mathvariant="double-struck"`,
|
|
150
|
+
* so Chrome would show plain letters, losing the difference between a vector and a scalar. MathML
|
|
151
|
+
* Core's own answer is to spell such letters with their Unicode math characters (𝐄, ℝ), which every
|
|
152
|
+
* browser renders. Identifiers and numbers only; anything without a Unicode form (Greek, text runs)
|
|
153
|
+
* keeps its attribute and gets the CSS fallback in the component stylesheet.
|
|
154
|
+
*/
|
|
155
|
+
function useUnicodeMathLetters(root) {
|
|
156
|
+
root.querySelectorAll('mi[mathvariant], mn[mathvariant]').forEach((node) => {
|
|
157
|
+
const variant = node.getAttribute('mathvariant') ?? '';
|
|
158
|
+
const text = node.textContent ?? '';
|
|
159
|
+
const letters = Array.from(text, (char) => toMathLetter(char, variant));
|
|
160
|
+
if (text && letters.every((letter) => letter !== null)) {
|
|
161
|
+
node.textContent = letters.join('');
|
|
162
|
+
node.removeAttribute('mathvariant');
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* TeX that defines macros. KaTeX's `maxExpand` counts expansions, not output, so three kilobytes of
|
|
168
|
+
* `\def` can expand to half a million MathML nodes and freeze the tab, again on every streamed
|
|
169
|
+
* chunk. A rendered answer has no use for its own macros, so such formulas stay source.
|
|
170
|
+
*/
|
|
171
|
+
const TEX_MACRO_DEFINITION = /\\(?:[gex]?def|let|futurelet|newcommand|renewcommand|providecommand)(?![a-zA-Z])/;
|
|
93
172
|
export class MarkdownService {
|
|
94
173
|
constructor() {
|
|
95
174
|
this.engine = new MarkdownEngine();
|
|
@@ -100,6 +179,10 @@ export class MarkdownService {
|
|
|
100
179
|
this.mermaidEngine = null;
|
|
101
180
|
/** In-flight load, so N diagrams in one document share ONE chunk fetch. */
|
|
102
181
|
this.mermaidLoad = null;
|
|
182
|
+
/** The KaTeX engine, once its lazy chunk has landed. Null until a formula is actually typeset. */
|
|
183
|
+
this.katexEngine = null;
|
|
184
|
+
/** In-flight load, so N formulas in one document share ONE chunk fetch. */
|
|
185
|
+
this.katexLoad = null;
|
|
103
186
|
/**
|
|
104
187
|
* Prism-backed highlight function injected into the core engine. The engine
|
|
105
188
|
* itself has no Prism dependency — it calls this for the HTML output path.
|
|
@@ -306,6 +389,99 @@ export class MarkdownService {
|
|
|
306
389
|
async renderMermaid(container) {
|
|
307
390
|
return this.RenderMermaid(container);
|
|
308
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Fetch the KaTeX engine, on first use only. A dynamic `import()` under the "genuine bundle-size
|
|
394
|
+
* deferral" allowance (typescript-style rules, category 3), like {@link loadMermaid}: KaTeX is
|
|
395
|
+
* 277 KB minified (76 KB gzipped), and documents with no math never download it. A failed load
|
|
396
|
+
* clears the cached promise so a later render can retry.
|
|
397
|
+
*/
|
|
398
|
+
async loadKatex() {
|
|
399
|
+
if (this.katexEngine) {
|
|
400
|
+
return this.katexEngine;
|
|
401
|
+
}
|
|
402
|
+
this.katexLoad ??= import('katex').then((module) => module.default);
|
|
403
|
+
try {
|
|
404
|
+
this.katexEngine = await this.katexLoad;
|
|
405
|
+
return this.katexEngine;
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
this.katexLoad = null;
|
|
409
|
+
throw error;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Spell MathML's styled letters (`mathvariant="bold"`, `"double-struck"`, ...) with their Unicode
|
|
414
|
+
* math characters, which Chrome renders. `RenderMath` already does this; call it on its own when
|
|
415
|
+
* math is off, because MathML that arrived as markup still renders then.
|
|
416
|
+
*
|
|
417
|
+
* @param container The DOM element containing the MathML
|
|
418
|
+
*/
|
|
419
|
+
UseUnicodeMathLetters(container) {
|
|
420
|
+
useUnicodeMathLetters(container);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Typeset TeX math in a container: every `code.mj-math` placeholder that markdown-core's
|
|
424
|
+
* math extension emitted. Call this after the HTML has been inserted into the DOM. MathML that
|
|
425
|
+
* arrived as markup is already rendered by the browser; it only gets the letter fix below.
|
|
426
|
+
*
|
|
427
|
+
* The output is MathML, which current browsers lay out natively, so hosts need no KaTeX
|
|
428
|
+
* stylesheet or fonts. KaTeX builds the nodes itself from the TeX text (no HTML string is
|
|
429
|
+
* parsed) and runs with `trust` off, so `\href`, `\url` and `\includegraphics` stay inert.
|
|
430
|
+
* A formula KaTeX cannot parse, one that defines macros, or an engine that fails to load leaves
|
|
431
|
+
* the TeX source showing as code. User sizes are capped at 20em.
|
|
432
|
+
*
|
|
433
|
+
* Whether math is on is the caller's decision (`<mj-markdown>` checks its `EnableMath` input):
|
|
434
|
+
* this service is a root singleton whose config the last-rendered component set, so it is no
|
|
435
|
+
* guide to the container in hand. Without math enabled at parse time there are no placeholders.
|
|
436
|
+
*
|
|
437
|
+
* @param container The DOM element containing the math placeholders
|
|
438
|
+
* @returns true if at least one TeX formula was typeset
|
|
439
|
+
*/
|
|
440
|
+
async RenderMath(container) {
|
|
441
|
+
// MathML that arrived as markup (a formula from a JATS article) needs the same Chrome fix as
|
|
442
|
+
// KaTeX's output, and no engine.
|
|
443
|
+
useUnicodeMathLetters(container);
|
|
444
|
+
// Find the placeholders BEFORE touching the engine: no math, no chunk fetch.
|
|
445
|
+
const placeholders = container.querySelectorAll('code.mj-math');
|
|
446
|
+
if (placeholders.length === 0)
|
|
447
|
+
return false;
|
|
448
|
+
let engine;
|
|
449
|
+
try {
|
|
450
|
+
engine = await this.loadKatex();
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
console.error('Failed to load the KaTeX math engine:', error);
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
let typeset = false;
|
|
457
|
+
placeholders.forEach((placeholder) => {
|
|
458
|
+
// A newer render (the next streamed chunk) replaced it while KaTeX was loading.
|
|
459
|
+
if (!container.contains(placeholder))
|
|
460
|
+
return;
|
|
461
|
+
const tex = placeholder.textContent ?? '';
|
|
462
|
+
const target = document.createElement('span');
|
|
463
|
+
try {
|
|
464
|
+
if (TEX_MACRO_DEFINITION.test(tex)) {
|
|
465
|
+
throw new Error('Formulas that define macros are not typeset');
|
|
466
|
+
}
|
|
467
|
+
engine.render(tex, target, {
|
|
468
|
+
displayMode: placeholder.classList.contains('mj-math-display'),
|
|
469
|
+
output: 'mathml',
|
|
470
|
+
throwOnError: true,
|
|
471
|
+
strict: 'ignore',
|
|
472
|
+
maxSize: 20
|
|
473
|
+
});
|
|
474
|
+
useUnicodeMathLetters(target);
|
|
475
|
+
placeholder.replaceWith(...Array.from(target.childNodes));
|
|
476
|
+
typeset = true;
|
|
477
|
+
}
|
|
478
|
+
catch (error) {
|
|
479
|
+
placeholder.classList.add('mj-math-error');
|
|
480
|
+
placeholder.title = error instanceof Error ? error.message : 'This formula could not be typeset';
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
return typeset;
|
|
484
|
+
}
|
|
309
485
|
/**
|
|
310
486
|
* Mark mermaid blocks as un-renderable, so the failure is VISIBLE rather than a block of
|
|
311
487
|
* raw diagram source the reader can't explain. The `.mermaid-error` class carries the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.service.js","sourceRoot":"","sources":["../../../src/lib/services/markdown.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport Prism from 'prismjs';\n// TYPE-ONLY — erased at compile time, so it costs nothing at runtime. The mermaid engine\n// itself is fetched on demand; see `loadMermaid()`.\nimport type { Mermaid } from 'mermaid';\nimport {\n MarkdownEngine,\n MarkdownConfig,\n DEFAULT_MARKDOWN_CONFIG,\n ResolvedMarkdownConfig,\n HeadingInfo,\n HighlightFunction\n} from '@memberjunction/markdown-core';\nimport DOMPurify from 'dompurify';\nimport { escapeHtml } from '@memberjunction/markdown-core';\n\n// Import common Prism language components\n// Additional languages can be imported by the consuming application\nimport 'prismjs/components/prism-typescript';\nimport 'prismjs/components/prism-javascript';\nimport 'prismjs/components/prism-css';\nimport 'prismjs/components/prism-scss';\nimport 'prismjs/components/prism-json';\nimport 'prismjs/components/prism-bash';\nimport 'prismjs/components/prism-sql';\nimport 'prismjs/components/prism-python';\nimport 'prismjs/components/prism-csharp';\nimport 'prismjs/components/prism-java';\nimport 'prismjs/components/prism-markup';\nimport 'prismjs/components/prism-yaml';\nimport 'prismjs/components/prism-markdown';\nimport 'prismjs/components/prism-graphql';\n\n/**\n * Service for parsing and rendering markdown content on the web.\n *\n * The parsing/configuration work (marked setup, custom extensions, HTML and\n * token output) is owned by the framework-agnostic `@memberjunction/markdown-core`\n * {@link MarkdownEngine}. This Angular service is the web shell around it: it\n * injects a Prism-based highlighter into the engine and keeps the browser-only\n * concerns here — Mermaid rendering, copy buttons, and the DOM-based fixup of\n * HTML that marked miscoded as a code block.\n */\n/**\n * The DOMPurify instance used for rendered HTML, created once per window and configured\n * once. A private instance rather than the shared default so the hooks below cannot leak\n * into other packages' DOMPurify usage, and so the configuration is parsed once instead\n * of on every render.\n *\n * Profile: HTML + SVG + SVG filters, which keeps structural markup, inline styles, data\n * attributes and inline vector graphics and removes the script vectors. Deviations from\n * DOMPurify's defaults, each deliberate:\n * - FORCE_BODY: a document that starts with `<style>` keeps it (otherwise the parser hoists\n * a leading style element into <head> and it is dropped); agent-authored mockups start\n * that way.\n * - SANITIZE_DOM off: DOMPurify would strip any id or name that collides with a document\n * or form property (`title`, `name`, `location`, ...), which are exactly the slugs\n * heading ids produce. This app does not read globals off `window` by element id, so\n * DOM clobbering has no effect here, and heading anchors matter.\n * - `target` on links is kept, with `rel=\"noopener noreferrer\"` enforced when it is set.\n * - `<use>` is allowed only with a same-document reference (`#id`); external references\n * are removed. SMIL `<animate>`/`<set>` stay disallowed: they can rewrite `href`.\n */\nlet purifier: ReturnType<typeof DOMPurify> | null = null;\n\nfunction getPurifier(): ReturnType<typeof DOMPurify> | null {\n if (purifier) return purifier;\n if (typeof window === 'undefined') return null;\n const instance = DOMPurify(window);\n if (!instance.isSupported) return null;\n instance.setConfig({\n USE_PROFILES: { html: true, svg: true, svgFilters: true },\n ADD_TAGS: ['use'],\n ADD_ATTR: ['target'],\n FORCE_BODY: true,\n SANITIZE_DOM: false,\n });\n instance.addHook('uponSanitizeAttribute', (node, data) => {\n const name = data.attrName.toLowerCase();\n if (node.nodeName.toLowerCase() === 'use' && (name === 'href' || name === 'xlink:href')) {\n // Same-document sprite references only. Anything else can load an external document.\n data.keepAttr = data.attrValue.trim().startsWith('#');\n }\n });\n instance.addHook('afterSanitizeAttributes', (node) => {\n const tag = node.nodeName.toLowerCase();\n if (tag === 'a' && node.hasAttribute('target')) {\n node.setAttribute('rel', 'noopener noreferrer');\n }\n if (tag === 'use') {\n // The attribute hook above has already dropped any non-fragment reference; a <use>\n // with nothing left to reference is removed rather than left as an empty element.\n const ref = node.getAttribute('href') ?? node.getAttribute('xlink:href');\n if (!ref || !ref.trim().startsWith('#')) {\n node.parentNode?.removeChild(node);\n }\n }\n });\n purifier = instance;\n return purifier;\n}\n\n@Injectable({\n providedIn: 'root'\n})\nexport class MarkdownService {\n private engine = new MarkdownEngine();\n private mermaidInitialized = false;\n private lastMermaidTheme: string | null = null;\n private currentConfig: ResolvedMarkdownConfig = { ...DEFAULT_MARKDOWN_CONFIG };\n /** The mermaid engine, once its lazy chunk has landed. Null until a diagram is actually rendered. */\n private mermaidEngine: Mermaid | null = null;\n /** In-flight load, so N diagrams in one document share ONE chunk fetch. */\n private mermaidLoad: Promise<Mermaid> | null = null;\n\n /**\n * Prism-backed highlight function injected into the core engine. The engine\n * itself has no Prism dependency — it calls this for the HTML output path.\n */\n private prismHighlight: HighlightFunction = (code: string, lang: string): string => {\n if (lang && Prism.languages[lang]) {\n try {\n return Prism.highlight(code, Prism.languages[lang], lang);\n } catch (e) {\n console.warn(`Prism highlighting failed for language: ${lang}`, e);\n }\n }\n // Return code as-is if language not found or highlighting fails\n return code;\n };\n\n constructor() {\n this.ConfigureMarked(this.currentConfig);\n }\n\n /**\n * Configure the underlying engine with the provided options.\n */\n public ConfigureMarked(config: MarkdownConfig): void {\n this.currentConfig = { ...DEFAULT_MARKDOWN_CONFIG, ...config };\n this.engine.configureMarked(this.currentConfig, { highlightFn: this.prismHighlight });\n }\n\n /** @deprecated Use {@link ConfigureMarked}. */\n public configureMarked(config: MarkdownConfig): void {\n return this.ConfigureMarked(config);\n }\n\n /**\n * Parse markdown to HTML.\n * @param markdown The markdown string to parse\n * @param config Optional config overrides for this parse operation\n * @returns The rendered HTML string\n */\n public Parse(markdown: string, config?: Partial<MarkdownConfig>): string {\n if (!markdown) return '';\n\n // Apply config overrides if provided (re-injects the Prism highlighter)\n if (config) {\n this.ConfigureMarked({ ...this.currentConfig, ...config });\n }\n\n let html = this.engine.parseToHtml(markdown);\n\n // When HTML passthrough is enabled, fix incorrectly code-wrapped HTML.\n // marked sometimes wraps inline HTML in <pre><code> blocks. This is a\n // DOM-based fixup so it stays in the web service, not the core engine.\n if (this.currentConfig.enableHtml) {\n html = this.unwrapMiscodedHtml(html);\n }\n\n // The service is the one place every consumer passes through (the component, the chat\n // widget, anything binding parse() output to innerHTML), so the sanitizer runs here.\n // enableJavaScript is the explicit opt-out.\n if (!this.currentConfig.enableJavaScript) {\n html = this.SanitizeHtml(html);\n }\n\n return html;\n }\n\n /** @deprecated Use {@link Parse}. */\n public parse(markdown: string, config?: Partial<MarkdownConfig>): string {\n return this.Parse(markdown, config);\n }\n\n /**\n * Sanitize rendered HTML for binding to innerHTML while preserving layout HTML, inline\n * styles and inline SVG. See the DOMPurify configuration at the top of this file for\n * what is kept and removed. Where no DOM is available to sanitize with, the markup is\n * escaped and rendered as text rather than trusted.\n */\n public SanitizeHtml(html: string): string {\n const instance = getPurifier();\n if (!instance) {\n return escapeHtml(html);\n }\n return instance.sanitize(html);\n }\n\n /** @deprecated Use {@link SanitizeHtml}. */\n public sanitizeHtml(html: string): string {\n return this.SanitizeHtml(html);\n }\n\n /**\n * Parse markdown asynchronously (useful for large documents)\n */\n public async ParseAsync(markdown: string, config?: Partial<MarkdownConfig>): Promise<string> {\n return this.Parse(markdown, config);\n }\n\n /** @deprecated Use {@link ParseAsync}. */\n public async parseAsync(markdown: string, config?: Partial<MarkdownConfig>): Promise<string> {\n return this.ParseAsync(markdown, config);\n }\n\n /**\n * Resolve the effective mermaid theme.\n * 'auto' maps to 'dark' or 'default' based on the document's data-theme attribute.\n */\n private resolveEffectiveMermaidTheme(): 'default' | 'dark' | 'forest' | 'neutral' | 'base' {\n const configTheme = this.currentConfig.mermaidTheme;\n if (configTheme !== 'auto') {\n return configTheme;\n }\n const isDark = typeof document !== 'undefined'\n && document.documentElement.getAttribute('data-theme') === 'dark';\n return isDark ? 'dark' : 'default';\n }\n\n /**\n * Fetch the mermaid engine, on first use only.\n *\n * MEASURED BUNDLE DEFERRAL — this is deliberately a dynamic `import()`, NOT a top-level\n * one, and must not be \"tidied\" back into a static import. `mermaid` and the parsing /\n * graph engines it pulls with it (`@mermaid-js/parser`, `langium`, `chevrotain`,\n * `cytoscape`, `katex`, `d3`) were ~0.95 MB raw of every host's initial download even\n * when no document ever contained a diagram. That is a rounding error for an in-app\n * host and the whole cost for a CDN-loaded embedding of the realtime session overlay\n * (MJ #3882). Documents with no diagram now never fetch the chunk at all.\n *\n * The in-flight promise is cached so N diagrams in one document share one fetch; a\n * FAILED load clears it, so a later render can retry. Retries are bounded by\n * user-driven render calls — there is no loop here.\n */\n private async loadMermaid(): Promise<Mermaid> {\n if (this.mermaidEngine) {\n return this.mermaidEngine;\n }\n\n this.mermaidLoad ??= import('mermaid').then((module) => module.default);\n\n try {\n this.mermaidEngine = await this.mermaidLoad;\n return this.mermaidEngine;\n } catch (error) {\n this.mermaidLoad = null;\n throw error;\n }\n }\n\n /**\n * Initialize Mermaid with the current theme configuration.\n * Re-initializes when the effective theme changes.\n * @param mermaid The lazily-loaded mermaid engine (see {@link loadMermaid})\n */\n private initializeMermaid(mermaid: Mermaid): void {\n const effectiveTheme = this.resolveEffectiveMermaidTheme();\n\n if (this.mermaidInitialized && this.lastMermaidTheme === effectiveTheme) {\n return;\n }\n\n mermaid.initialize({\n startOnLoad: false,\n theme: effectiveTheme,\n securityLevel: 'loose',\n fontFamily: 'inherit',\n suppressErrorRendering: true // Suppress visual error diagrams - errors go to console only\n });\n\n this.mermaidInitialized = true;\n this.lastMermaidTheme = effectiveTheme;\n }\n\n /**\n * Render Mermaid diagrams in a container element\n * Call this after the HTML has been inserted into the DOM\n * @param container The DOM element containing mermaid code blocks\n */\n public async RenderMermaid(container: HTMLElement): Promise<boolean> {\n if (!this.currentConfig.enableMermaid) return false;\n\n // Find all mermaid code blocks BEFORE touching the engine — this is the branch the\n // deferral hangs off: no diagrams in this document means no chunk fetch at all.\n const mermaidBlocks = container.querySelectorAll('pre > code.language-mermaid, .mermaid');\n\n if (mermaidBlocks.length === 0) return false;\n\n let mermaid: Mermaid;\n try {\n mermaid = await this.loadMermaid();\n } catch (error) {\n // The chunk could not be fetched (offline, blocked host, bad deploy). Say so on\n // every block we were going to render — an empty panel would look like the diagram\n // simply wasn't there.\n console.error('Failed to load the Mermaid diagram engine:', error);\n this.markMermaidBlocksFailed(mermaidBlocks, 'Diagram engine failed to load');\n return false;\n }\n\n this.initializeMermaid(mermaid);\n\n for (let i = 0; i < mermaidBlocks.length; i++) {\n const block = mermaidBlocks[i];\n const code = block.textContent || '';\n\n if (!code.trim()) continue;\n\n try {\n // Create a unique ID for this diagram\n const id = `mermaid-${Date.now()}-${i}`;\n\n // Render the diagram\n const { svg } = await mermaid.render(id, code);\n\n // Replace the code block with the rendered SVG\n const wrapper = document.createElement('div');\n wrapper.className = 'mermaid-diagram';\n wrapper.innerHTML = svg;\n\n // Replace the pre element (parent of code) or the mermaid element itself\n const elementToReplace = block.tagName === 'CODE' ? block.parentElement : block;\n elementToReplace?.parentNode?.replaceChild(wrapper, elementToReplace);\n } catch (error) {\n console.warn('Mermaid rendering failed:', error);\n // Show it failed on this block — the source stays visible underneath.\n this.markMermaidBlocksFailed([block], 'Diagram rendering failed');\n }\n }\n\n return true;\n }\n\n /** @deprecated Use {@link RenderMermaid}. */\n public async renderMermaid(container: HTMLElement): Promise<boolean> {\n return this.RenderMermaid(container);\n }\n\n /**\n * Mark mermaid blocks as un-renderable, so the failure is VISIBLE rather than a block of\n * raw diagram source the reader can't explain. The `.mermaid-error` class carries the\n * error styling and a default caption; `data-mermaid-error` overrides that caption with\n * the specific reason (see `.mermaid-error[data-mermaid-error]::before` in the component\n * stylesheet).\n *\n * @param blocks The mermaid code blocks (or `.mermaid` elements) that could not be rendered\n * @param message Short reason shown above the un-rendered source\n */\n private markMermaidBlocksFailed(blocks: ArrayLike<Element>, message: string): void {\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const target = block.tagName === 'CODE' ? block.parentElement : block;\n target?.classList.add('mermaid-error');\n target?.setAttribute('data-mermaid-error', message);\n }\n }\n\n /**\n * Highlight code blocks with Prism\n * Call this after the HTML has been inserted into the DOM\n * @param container The DOM element containing code blocks\n */\n public HighlightCode(container: HTMLElement): void {\n if (!this.currentConfig.enableHighlight) return;\n\n // Prism.highlightAllUnder handles finding and highlighting code blocks\n Prism.highlightAllUnder(container);\n }\n\n /** @deprecated Use {@link HighlightCode}. */\n public highlightCode(container: HTMLElement): void {\n return this.HighlightCode(container);\n }\n\n /**\n * Add copy buttons to code blocks\n * @param container The DOM element containing code blocks\n */\n public AddCodeCopyButtons(container: HTMLElement): void {\n if (!this.currentConfig.enableCodeCopy) return;\n\n const codeBlocks = container.querySelectorAll('pre > code');\n\n codeBlocks.forEach((codeBlock) => {\n const pre = codeBlock.parentElement;\n if (!pre || pre.querySelector('.code-copy-btn')) return; // Already has button\n\n // Create copy button\n const button = document.createElement('button');\n button.className = 'code-copy-btn';\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n button.title = 'Copy code';\n button.type = 'button';\n\n button.addEventListener('click', async () => {\n const code = codeBlock.textContent || '';\n\n try {\n await navigator.clipboard.writeText(code);\n button.innerHTML = '<i class=\"fas fa-check\"></i>';\n button.classList.add('copied');\n\n setTimeout(() => {\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n button.classList.remove('copied');\n }, 2000);\n } catch (err) {\n console.error('Failed to copy code:', err);\n button.innerHTML = '<i class=\"fas fa-times\"></i>';\n\n setTimeout(() => {\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n }, 2000);\n }\n });\n\n // Add toolbar wrapper\n const toolbar = document.createElement('div');\n toolbar.className = 'code-toolbar';\n toolbar.appendChild(button);\n\n // Make pre position relative for absolute positioning of toolbar\n pre.style.position = 'relative';\n pre.appendChild(toolbar);\n });\n }\n\n /** @deprecated Use {@link AddCodeCopyButtons}. */\n public addCodeCopyButtons(container: HTMLElement): void {\n return this.AddCodeCopyButtons(container);\n }\n\n /**\n * Initialize collapsible heading functionality\n * This method is a no-op - the component handles event binding\n * @param container The DOM element containing collapsible sections\n */\n public InitializeCollapsibleHeadings(_container: HTMLElement): void {\n // Event binding is handled by the component's setupCollapsibleListeners\n // This method exists for API compatibility but does nothing\n }\n\n /** @deprecated Use {@link InitializeCollapsibleHeadings}. */\n public initializeCollapsibleHeadings(_container: HTMLElement): void {\n return this.InitializeCollapsibleHeadings(_container);\n }\n\n /**\n * Get the list of headings from the last parsed document\n * Useful for building table of contents\n */\n public GetHeadingList(): HeadingInfo[] {\n return this.engine.getHeadingList();\n }\n\n /** @deprecated Use {@link GetHeadingList}. */\n public getHeadingList(): HeadingInfo[] {\n return this.GetHeadingList();\n }\n\n /**\n * Get the current configuration\n */\n public GetConfig(): ResolvedMarkdownConfig {\n return this.engine.getConfig();\n }\n\n /** @deprecated Use {@link GetConfig}. */\n public getConfig(): ResolvedMarkdownConfig {\n return this.GetConfig();\n }\n\n /**\n * Reset configuration to defaults\n */\n public ResetConfig(): void {\n this.ConfigureMarked(DEFAULT_MARKDOWN_CONFIG);\n }\n\n /** @deprecated Use {@link ResetConfig}. */\n public resetConfig(): void {\n return this.ResetConfig();\n }\n\n /**\n * Check if a language is supported by Prism\n */\n public IsLanguageSupported(lang: string): boolean {\n return !!Prism.languages[lang];\n }\n\n /** @deprecated Use {@link IsLanguageSupported}. */\n public isLanguageSupported(lang: string): boolean {\n return this.IsLanguageSupported(lang);\n }\n\n /**\n * Get list of supported Prism languages\n */\n public GetSupportedLanguages(): string[] {\n return Object.keys(Prism.languages).filter(\n lang => typeof Prism.languages[lang] === 'object'\n );\n }\n\n /** @deprecated Use {@link GetSupportedLanguages}. */\n public getSupportedLanguages(): string[] {\n return this.GetSupportedLanguages();\n }\n\n /**\n * Fix HTML that was incorrectly wrapped in <pre><code> blocks by marked.\n * This happens when marked interprets inline HTML (especially indented HTML)\n * as code blocks. We detect this by checking if the code block content\n * looks like valid HTML structure rather than actual code.\n *\n * Only processes code blocks WITHOUT a language class (e.g., language-javascript)\n * to avoid unwrapping intentional code examples.\n */\n private unwrapMiscodedHtml(html: string): string {\n // Quick check - if no pre tags, nothing to do\n if (!html.includes('<pre>')) {\n return html;\n }\n\n // Skip if SVG is present - DOMParser mangles SVG elements like <rect>\n // when parsing as 'text/html' due to namespace issues\n if (html.includes('<svg')) {\n return html;\n }\n\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html');\n const container = doc.body.firstChild as HTMLElement;\n\n if (!container) return html;\n\n // Find all pre > code elements WITHOUT a language class\n // Code blocks with language classes (language-javascript, etc.) are intentional\n const preElements = container.querySelectorAll('pre');\n let modified = false;\n\n for (const pre of Array.from(preElements)) {\n const code = pre.querySelector('code');\n if (!code) continue;\n\n // Skip if code has a language class - it's intentional code\n const hasLanguageClass = code.className && /language-\\w+/.test(code.className);\n if (hasLanguageClass) continue;\n\n // Get the text content (this is HTML-decoded by the browser)\n const content = code.textContent?.trim() || '';\n\n // Check if this looks like HTML that was incorrectly wrapped\n if (this.looksLikeStructuralHtml(content)) {\n // Verify it parses as valid HTML with actual elements\n const testDoc = parser.parseFromString(content, 'text/html');\n const hasStructure = testDoc.body.children.length > 0 ||\n (testDoc.body.innerHTML.trim().length > 0 &&\n testDoc.body.innerHTML.includes('<'));\n\n if (hasStructure) {\n // Replace the <pre> with the actual HTML content. The nodes are built inside a\n // <template> of the inert DOMParser document, never on the live `document`:\n // setting innerHTML on a live element fires image error handlers at parse time,\n // before the sanitizer in parse() has seen the markup. A <template> also parses\n // in body context, so a mockup that starts with <style> keeps it (a full-document\n // parse would hoist it into <head> and drop it).\n const template = doc.createElement('template');\n template.innerHTML = content;\n pre.replaceWith(template.content);\n modified = true;\n }\n }\n }\n\n if (modified) {\n return container.innerHTML;\n }\n } catch (error) {\n console.warn('Error in unwrapMiscodedHtml:', error);\n }\n\n return html;\n }\n\n /**\n * Check if content looks like structural HTML that was incorrectly\n * wrapped in a code block. We look for common HTML element patterns\n * that indicate this is meant to be rendered HTML, not code.\n */\n private looksLikeStructuralHtml(content: string): boolean {\n // Must start with < to be HTML\n if (!content.startsWith('<')) return false;\n\n // Must end with > (closing tag)\n if (!content.endsWith('>')) return false;\n\n // Check for common structural HTML tags that indicate layout HTML\n // These are tags that would typically appear in a UI mockup/prototype\n const structuralTagPattern = /<(div|span|table|tr|td|th|thead|tbody|p|ul|ol|li|section|article|header|footer|nav|main|aside|form|input|button|label|select|option|textarea|h[1-6]|img|a|strong|em|b|i|br|hr)\\b/i;\n\n if (!structuralTagPattern.test(content)) return false;\n\n // Additional check: should have multiple tags or nested structure\n // Single self-closing tags like <br> or <img> shouldn't trigger unwrapping\n const tagCount = (content.match(/<\\w+/g) || []).length;\n if (tagCount < 2) return false;\n\n // Check it's not just showing HTML as an example (common in docs)\n // If content has lots of < or > it's probably escaped HTML being shown\n if (content.includes('<') || content.includes('>')) return false;\n\n return true;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"markdown.service.js","sourceRoot":"","sources":["../../../src/lib/services/markdown.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport Prism from 'prismjs';\n// TYPE-ONLY — erased at compile time, so it costs nothing at runtime. The mermaid engine\n// itself is fetched on demand; see `loadMermaid()`.\nimport type { Mermaid } from 'mermaid';\n// TYPE-ONLY as well: KaTeX is fetched on demand the same way; see `loadKatex()`.\nimport type katex from 'katex';\nimport {\n MarkdownEngine,\n MarkdownConfig,\n DEFAULT_MARKDOWN_CONFIG,\n ResolvedMarkdownConfig,\n HeadingInfo,\n HighlightFunction\n} from '@memberjunction/markdown-core';\nimport DOMPurify from 'dompurify';\nimport { escapeHtml } from '@memberjunction/markdown-core';\n\n// Import common Prism language components\n// Additional languages can be imported by the consuming application\nimport 'prismjs/components/prism-typescript';\nimport 'prismjs/components/prism-javascript';\nimport 'prismjs/components/prism-css';\nimport 'prismjs/components/prism-scss';\nimport 'prismjs/components/prism-json';\nimport 'prismjs/components/prism-bash';\nimport 'prismjs/components/prism-sql';\nimport 'prismjs/components/prism-python';\nimport 'prismjs/components/prism-csharp';\nimport 'prismjs/components/prism-java';\nimport 'prismjs/components/prism-markup';\nimport 'prismjs/components/prism-yaml';\nimport 'prismjs/components/prism-markdown';\nimport 'prismjs/components/prism-graphql';\n\n/**\n * Service for parsing and rendering markdown content on the web.\n *\n * The parsing/configuration work (marked setup, custom extensions, HTML and\n * token output) is owned by the framework-agnostic `@memberjunction/markdown-core`\n * {@link MarkdownEngine}. This Angular service is the web shell around it: it\n * injects a Prism-based highlighter into the engine and keeps the browser-only\n * concerns here — Mermaid rendering, copy buttons, and the DOM-based fixup of\n * HTML that marked miscoded as a code block.\n */\n/**\n * The DOMPurify instance used for rendered HTML, created once per window and configured\n * once. A private instance rather than the shared default so the hooks below cannot leak\n * into other packages' DOMPurify usage, and so the configuration is parsed once instead\n * of on every render.\n *\n * Profile: HTML + SVG + SVG filters + MathML, which keeps structural markup, inline styles, data\n * attributes, inline vector graphics and formulas written as MathML, and removes the script vectors.\n * MathML is DOMPurify's own profile, with its namespace checks (the mXSS class of bypasses through\n * `<math>` is what those checks exist for). Deviations from\n * DOMPurify's defaults, each deliberate:\n * - FORCE_BODY: a document that starts with `<style>` keeps it (otherwise the parser hoists\n * a leading style element into <head> and it is dropped); agent-authored mockups start\n * that way.\n * - SANITIZE_DOM off: DOMPurify would strip any id or name that collides with a document\n * or form property (`title`, `name`, `location`, ...), which are exactly the slugs\n * heading ids produce. This app does not read globals off `window` by element id, so\n * DOM clobbering has no effect here, and heading anchors matter.\n * - `target` on links is kept, with `rel=\"noopener noreferrer\"` enforced when it is set.\n * - `columnalign` and `columnspacing` are added: DOMPurify's MathML list spells the first\n * `columnsalign` and lacks the second, so MathML tables lost their layout in Firefox and Safari\n * (Chrome ignores both; the component stylesheet aligns columns there). Both are presentational. (`form` on `<mo>` stays out: on HTML it associates an element with a form, and\n * MathML infers an operator's form without it.)\n * - `annotation` / `annotation-xml` content is dropped with the element. DOMPurify removes both but\n * would keep their text, and MathJax, Pandoc and Word MathML carry the TeX source there, so a\n * formula would read \"x2x^2\".\n * - `<use>` is allowed only with a same-document reference (`#id`); external references\n * are removed. SMIL `<animate>`/`<set>` stay disallowed: they can rewrite `href`.\n */\nlet purifier: ReturnType<typeof DOMPurify> | null = null;\n\nfunction getPurifier(): ReturnType<typeof DOMPurify> | null {\n if (purifier) return purifier;\n if (typeof window === 'undefined') return null;\n const instance = DOMPurify(window);\n if (!instance.isSupported) return null;\n instance.setConfig({\n USE_PROFILES: { html: true, svg: true, svgFilters: true, mathMl: true },\n ADD_TAGS: ['use'],\n ADD_ATTR: ['target', 'columnalign', 'columnspacing'],\n ADD_FORBID_CONTENTS: ['annotation', 'annotation-xml'],\n FORCE_BODY: true,\n SANITIZE_DOM: false,\n });\n instance.addHook('uponSanitizeAttribute', (node, data) => {\n const name = data.attrName.toLowerCase();\n if (node.nodeName.toLowerCase() === 'use' && (name === 'href' || name === 'xlink:href')) {\n // Same-document sprite references only. Anything else can load an external document.\n data.keepAttr = data.attrValue.trim().startsWith('#');\n }\n });\n instance.addHook('afterSanitizeAttributes', (node) => {\n const tag = node.nodeName.toLowerCase();\n if (tag === 'a' && node.hasAttribute('target')) {\n node.setAttribute('rel', 'noopener noreferrer');\n }\n if (tag === 'use') {\n // The attribute hook above has already dropped any non-fragment reference; a <use>\n // with nothing left to reference is removed rather than left as an empty element.\n const ref = node.getAttribute('href') ?? node.getAttribute('xlink:href');\n if (!ref || !ref.trim().startsWith('#')) {\n node.parentNode?.removeChild(node);\n }\n }\n });\n purifier = instance;\n return purifier;\n}\n\n/**\n * Unicode's Mathematical Alphanumeric Symbols by MathML `mathvariant`: the code points of capital\n * A, small a and digit 0 (0 where Unicode has no digits in that style).\n */\nconst MATH_LETTER_BASES: Readonly<Record<string, readonly [number, number, number]>> = {\n bold: [0x1d400, 0x1d41a, 0x1d7ce],\n italic: [0x1d434, 0x1d44e, 0],\n 'bold-italic': [0x1d468, 0x1d482, 0],\n script: [0x1d49c, 0x1d4b6, 0],\n 'bold-script': [0x1d4d0, 0x1d4ea, 0],\n fraktur: [0x1d504, 0x1d51e, 0],\n 'double-struck': [0x1d538, 0x1d552, 0x1d7d8],\n 'bold-fraktur': [0x1d56c, 0x1d586, 0],\n 'sans-serif': [0x1d5a0, 0x1d5ba, 0x1d7e2],\n 'bold-sans-serif': [0x1d5d4, 0x1d5ee, 0x1d7ec],\n 'sans-serif-italic': [0x1d608, 0x1d622, 0],\n 'sans-serif-bold-italic': [0x1d63c, 0x1d656, 0],\n monospace: [0x1d670, 0x1d68a, 0x1d7f6],\n};\n\n/** Letters Unicode encoded (in Letterlike Symbols) before that block existed; it has holes where they would go. */\nconst MATH_LETTER_HOLES: Readonly<Record<string, string>> = {\n 'italic h': 'ℎ',\n 'script B': 'ℬ', 'script E': 'ℰ', 'script F': 'ℱ', 'script H': 'ℋ', 'script I': 'ℐ', 'script L': 'ℒ',\n 'script M': 'ℳ', 'script R': 'ℛ', 'script e': 'ℯ', 'script g': 'ℊ', 'script o': 'ℴ',\n 'fraktur C': 'ℭ', 'fraktur H': 'ℌ', 'fraktur I': 'ℑ', 'fraktur R': 'ℜ', 'fraktur Z': 'ℨ',\n 'double-struck C': 'ℂ', 'double-struck H': 'ℍ', 'double-struck N': 'ℕ', 'double-struck P': 'ℙ',\n 'double-struck Q': 'ℚ', 'double-struck R': 'ℝ', 'double-struck Z': 'ℤ',\n};\n\nfunction toMathLetter(char: string, variant: string): string | null {\n const hole = MATH_LETTER_HOLES[`${variant} ${char}`];\n if (hole) return hole;\n const bases = MATH_LETTER_BASES[variant];\n if (!bases) return null;\n const code = char.charCodeAt(0);\n if (code >= 65 && code <= 90) return String.fromCodePoint(bases[0] + code - 65);\n if (code >= 97 && code <= 122) return String.fromCodePoint(bases[1] + code - 97);\n if (code >= 48 && code <= 57 && bases[2]) return String.fromCodePoint(bases[2] + code - 48);\n return null;\n}\n\n/**\n * Chrome lays out MathML Core, which honors `mathvariant` only as \"normal\". KaTeX writes\n * \\mathbf{E} as `<mi mathvariant=\"bold\">E</mi>` and \\mathbb{R} as `mathvariant=\"double-struck\"`,\n * so Chrome would show plain letters, losing the difference between a vector and a scalar. MathML\n * Core's own answer is to spell such letters with their Unicode math characters (𝐄, ℝ), which every\n * browser renders. Identifiers and numbers only; anything without a Unicode form (Greek, text runs)\n * keeps its attribute and gets the CSS fallback in the component stylesheet.\n */\nfunction useUnicodeMathLetters(root: Element): void {\n root.querySelectorAll('mi[mathvariant], mn[mathvariant]').forEach((node) => {\n const variant = node.getAttribute('mathvariant') ?? '';\n const text = node.textContent ?? '';\n const letters = Array.from(text, (char) => toMathLetter(char, variant));\n if (text && letters.every((letter) => letter !== null)) {\n node.textContent = letters.join('');\n node.removeAttribute('mathvariant');\n }\n });\n}\n\n/**\n * TeX that defines macros. KaTeX's `maxExpand` counts expansions, not output, so three kilobytes of\n * `\\def` can expand to half a million MathML nodes and freeze the tab, again on every streamed\n * chunk. A rendered answer has no use for its own macros, so such formulas stay source.\n */\nconst TEX_MACRO_DEFINITION = /\\\\(?:[gex]?def|let|futurelet|newcommand|renewcommand|providecommand)(?![a-zA-Z])/;\n\n@Injectable({\n providedIn: 'root'\n})\nexport class MarkdownService {\n private engine = new MarkdownEngine();\n private mermaidInitialized = false;\n private lastMermaidTheme: string | null = null;\n private currentConfig: ResolvedMarkdownConfig = { ...DEFAULT_MARKDOWN_CONFIG };\n /** The mermaid engine, once its lazy chunk has landed. Null until a diagram is actually rendered. */\n private mermaidEngine: Mermaid | null = null;\n /** In-flight load, so N diagrams in one document share ONE chunk fetch. */\n private mermaidLoad: Promise<Mermaid> | null = null;\n /** The KaTeX engine, once its lazy chunk has landed. Null until a formula is actually typeset. */\n private katexEngine: typeof katex | null = null;\n /** In-flight load, so N formulas in one document share ONE chunk fetch. */\n private katexLoad: Promise<typeof katex> | null = null;\n\n /**\n * Prism-backed highlight function injected into the core engine. The engine\n * itself has no Prism dependency — it calls this for the HTML output path.\n */\n private prismHighlight: HighlightFunction = (code: string, lang: string): string => {\n if (lang && Prism.languages[lang]) {\n try {\n return Prism.highlight(code, Prism.languages[lang], lang);\n } catch (e) {\n console.warn(`Prism highlighting failed for language: ${lang}`, e);\n }\n }\n // Return code as-is if language not found or highlighting fails\n return code;\n };\n\n constructor() {\n this.ConfigureMarked(this.currentConfig);\n }\n\n /**\n * Configure the underlying engine with the provided options.\n */\n public ConfigureMarked(config: MarkdownConfig): void {\n this.currentConfig = { ...DEFAULT_MARKDOWN_CONFIG, ...config };\n this.engine.configureMarked(this.currentConfig, { highlightFn: this.prismHighlight });\n }\n\n /** @deprecated Use {@link ConfigureMarked}. */\n public configureMarked(config: MarkdownConfig): void {\n return this.ConfigureMarked(config);\n }\n\n /**\n * Parse markdown to HTML.\n * @param markdown The markdown string to parse\n * @param config Optional config overrides for this parse operation\n * @returns The rendered HTML string\n */\n public Parse(markdown: string, config?: Partial<MarkdownConfig>): string {\n if (!markdown) return '';\n\n // Apply config overrides if provided (re-injects the Prism highlighter)\n if (config) {\n this.ConfigureMarked({ ...this.currentConfig, ...config });\n }\n\n let html = this.engine.parseToHtml(markdown);\n\n // When HTML passthrough is enabled, fix incorrectly code-wrapped HTML.\n // marked sometimes wraps inline HTML in <pre><code> blocks. This is a\n // DOM-based fixup so it stays in the web service, not the core engine.\n if (this.currentConfig.enableHtml) {\n html = this.unwrapMiscodedHtml(html);\n }\n\n // The service is the one place every consumer passes through (the component, the chat\n // widget, anything binding parse() output to innerHTML), so the sanitizer runs here.\n // enableJavaScript is the explicit opt-out.\n if (!this.currentConfig.enableJavaScript) {\n html = this.SanitizeHtml(html);\n }\n\n return html;\n }\n\n /** @deprecated Use {@link Parse}. */\n public parse(markdown: string, config?: Partial<MarkdownConfig>): string {\n return this.Parse(markdown, config);\n }\n\n /**\n * Sanitize rendered HTML for binding to innerHTML while preserving layout HTML, inline\n * styles and inline SVG. See the DOMPurify configuration at the top of this file for\n * what is kept and removed. Where no DOM is available to sanitize with, the markup is\n * escaped and rendered as text rather than trusted.\n */\n public SanitizeHtml(html: string): string {\n const instance = getPurifier();\n if (!instance) {\n return escapeHtml(html);\n }\n return instance.sanitize(html);\n }\n\n /** @deprecated Use {@link SanitizeHtml}. */\n public sanitizeHtml(html: string): string {\n return this.SanitizeHtml(html);\n }\n\n /**\n * Parse markdown asynchronously (useful for large documents)\n */\n public async ParseAsync(markdown: string, config?: Partial<MarkdownConfig>): Promise<string> {\n return this.Parse(markdown, config);\n }\n\n /** @deprecated Use {@link ParseAsync}. */\n public async parseAsync(markdown: string, config?: Partial<MarkdownConfig>): Promise<string> {\n return this.ParseAsync(markdown, config);\n }\n\n /**\n * Resolve the effective mermaid theme.\n * 'auto' maps to 'dark' or 'default' based on the document's data-theme attribute.\n */\n private resolveEffectiveMermaidTheme(): 'default' | 'dark' | 'forest' | 'neutral' | 'base' {\n const configTheme = this.currentConfig.mermaidTheme;\n if (configTheme !== 'auto') {\n return configTheme;\n }\n const isDark = typeof document !== 'undefined'\n && document.documentElement.getAttribute('data-theme') === 'dark';\n return isDark ? 'dark' : 'default';\n }\n\n /**\n * Fetch the mermaid engine, on first use only.\n *\n * MEASURED BUNDLE DEFERRAL — this is deliberately a dynamic `import()`, NOT a top-level\n * one, and must not be \"tidied\" back into a static import. `mermaid` and the parsing /\n * graph engines it pulls with it (`@mermaid-js/parser`, `langium`, `chevrotain`,\n * `cytoscape`, `katex`, `d3`) were ~0.95 MB raw of every host's initial download even\n * when no document ever contained a diagram. That is a rounding error for an in-app\n * host and the whole cost for a CDN-loaded embedding of the realtime session overlay\n * (MJ #3882). Documents with no diagram now never fetch the chunk at all.\n *\n * The in-flight promise is cached so N diagrams in one document share one fetch; a\n * FAILED load clears it, so a later render can retry. Retries are bounded by\n * user-driven render calls — there is no loop here.\n */\n private async loadMermaid(): Promise<Mermaid> {\n if (this.mermaidEngine) {\n return this.mermaidEngine;\n }\n\n this.mermaidLoad ??= import('mermaid').then((module) => module.default);\n\n try {\n this.mermaidEngine = await this.mermaidLoad;\n return this.mermaidEngine;\n } catch (error) {\n this.mermaidLoad = null;\n throw error;\n }\n }\n\n /**\n * Initialize Mermaid with the current theme configuration.\n * Re-initializes when the effective theme changes.\n * @param mermaid The lazily-loaded mermaid engine (see {@link loadMermaid})\n */\n private initializeMermaid(mermaid: Mermaid): void {\n const effectiveTheme = this.resolveEffectiveMermaidTheme();\n\n if (this.mermaidInitialized && this.lastMermaidTheme === effectiveTheme) {\n return;\n }\n\n mermaid.initialize({\n startOnLoad: false,\n theme: effectiveTheme,\n securityLevel: 'loose',\n fontFamily: 'inherit',\n suppressErrorRendering: true // Suppress visual error diagrams - errors go to console only\n });\n\n this.mermaidInitialized = true;\n this.lastMermaidTheme = effectiveTheme;\n }\n\n /**\n * Render Mermaid diagrams in a container element\n * Call this after the HTML has been inserted into the DOM\n * @param container The DOM element containing mermaid code blocks\n */\n public async RenderMermaid(container: HTMLElement): Promise<boolean> {\n if (!this.currentConfig.enableMermaid) return false;\n\n // Find all mermaid code blocks BEFORE touching the engine — this is the branch the\n // deferral hangs off: no diagrams in this document means no chunk fetch at all.\n const mermaidBlocks = container.querySelectorAll('pre > code.language-mermaid, .mermaid');\n\n if (mermaidBlocks.length === 0) return false;\n\n let mermaid: Mermaid;\n try {\n mermaid = await this.loadMermaid();\n } catch (error) {\n // The chunk could not be fetched (offline, blocked host, bad deploy). Say so on\n // every block we were going to render — an empty panel would look like the diagram\n // simply wasn't there.\n console.error('Failed to load the Mermaid diagram engine:', error);\n this.markMermaidBlocksFailed(mermaidBlocks, 'Diagram engine failed to load');\n return false;\n }\n\n this.initializeMermaid(mermaid);\n\n for (let i = 0; i < mermaidBlocks.length; i++) {\n const block = mermaidBlocks[i];\n const code = block.textContent || '';\n\n if (!code.trim()) continue;\n\n try {\n // Create a unique ID for this diagram\n const id = `mermaid-${Date.now()}-${i}`;\n\n // Render the diagram\n const { svg } = await mermaid.render(id, code);\n\n // Replace the code block with the rendered SVG\n const wrapper = document.createElement('div');\n wrapper.className = 'mermaid-diagram';\n wrapper.innerHTML = svg;\n\n // Replace the pre element (parent of code) or the mermaid element itself\n const elementToReplace = block.tagName === 'CODE' ? block.parentElement : block;\n elementToReplace?.parentNode?.replaceChild(wrapper, elementToReplace);\n } catch (error) {\n console.warn('Mermaid rendering failed:', error);\n // Show it failed on this block — the source stays visible underneath.\n this.markMermaidBlocksFailed([block], 'Diagram rendering failed');\n }\n }\n\n return true;\n }\n\n /** @deprecated Use {@link RenderMermaid}. */\n public async renderMermaid(container: HTMLElement): Promise<boolean> {\n return this.RenderMermaid(container);\n }\n\n /**\n * Fetch the KaTeX engine, on first use only. A dynamic `import()` under the \"genuine bundle-size\n * deferral\" allowance (typescript-style rules, category 3), like {@link loadMermaid}: KaTeX is\n * 277 KB minified (76 KB gzipped), and documents with no math never download it. A failed load\n * clears the cached promise so a later render can retry.\n */\n private async loadKatex(): Promise<typeof katex> {\n if (this.katexEngine) {\n return this.katexEngine;\n }\n\n this.katexLoad ??= import('katex').then((module) => module.default);\n\n try {\n this.katexEngine = await this.katexLoad;\n return this.katexEngine;\n } catch (error) {\n this.katexLoad = null;\n throw error;\n }\n }\n\n /**\n * Spell MathML's styled letters (`mathvariant=\"bold\"`, `\"double-struck\"`, ...) with their Unicode\n * math characters, which Chrome renders. `RenderMath` already does this; call it on its own when\n * math is off, because MathML that arrived as markup still renders then.\n *\n * @param container The DOM element containing the MathML\n */\n public UseUnicodeMathLetters(container: HTMLElement): void {\n useUnicodeMathLetters(container);\n }\n\n /**\n * Typeset TeX math in a container: every `code.mj-math` placeholder that markdown-core's\n * math extension emitted. Call this after the HTML has been inserted into the DOM. MathML that\n * arrived as markup is already rendered by the browser; it only gets the letter fix below.\n *\n * The output is MathML, which current browsers lay out natively, so hosts need no KaTeX\n * stylesheet or fonts. KaTeX builds the nodes itself from the TeX text (no HTML string is\n * parsed) and runs with `trust` off, so `\\href`, `\\url` and `\\includegraphics` stay inert.\n * A formula KaTeX cannot parse, one that defines macros, or an engine that fails to load leaves\n * the TeX source showing as code. User sizes are capped at 20em.\n *\n * Whether math is on is the caller's decision (`<mj-markdown>` checks its `EnableMath` input):\n * this service is a root singleton whose config the last-rendered component set, so it is no\n * guide to the container in hand. Without math enabled at parse time there are no placeholders.\n *\n * @param container The DOM element containing the math placeholders\n * @returns true if at least one TeX formula was typeset\n */\n public async RenderMath(container: HTMLElement): Promise<boolean> {\n // MathML that arrived as markup (a formula from a JATS article) needs the same Chrome fix as\n // KaTeX's output, and no engine.\n useUnicodeMathLetters(container);\n\n // Find the placeholders BEFORE touching the engine: no math, no chunk fetch.\n const placeholders = container.querySelectorAll<HTMLElement>('code.mj-math');\n if (placeholders.length === 0) return false;\n\n let engine: typeof katex;\n try {\n engine = await this.loadKatex();\n } catch (error) {\n console.error('Failed to load the KaTeX math engine:', error);\n return false;\n }\n\n let typeset = false;\n placeholders.forEach((placeholder) => {\n // A newer render (the next streamed chunk) replaced it while KaTeX was loading.\n if (!container.contains(placeholder)) return;\n const tex = placeholder.textContent ?? '';\n const target = document.createElement('span');\n try {\n if (TEX_MACRO_DEFINITION.test(tex)) {\n throw new Error('Formulas that define macros are not typeset');\n }\n engine.render(tex, target, {\n displayMode: placeholder.classList.contains('mj-math-display'),\n output: 'mathml',\n throwOnError: true,\n strict: 'ignore',\n maxSize: 20\n });\n useUnicodeMathLetters(target);\n placeholder.replaceWith(...Array.from(target.childNodes));\n typeset = true;\n } catch (error) {\n placeholder.classList.add('mj-math-error');\n placeholder.title = error instanceof Error ? error.message : 'This formula could not be typeset';\n }\n });\n\n return typeset;\n }\n\n /**\n * Mark mermaid blocks as un-renderable, so the failure is VISIBLE rather than a block of\n * raw diagram source the reader can't explain. The `.mermaid-error` class carries the\n * error styling and a default caption; `data-mermaid-error` overrides that caption with\n * the specific reason (see `.mermaid-error[data-mermaid-error]::before` in the component\n * stylesheet).\n *\n * @param blocks The mermaid code blocks (or `.mermaid` elements) that could not be rendered\n * @param message Short reason shown above the un-rendered source\n */\n private markMermaidBlocksFailed(blocks: ArrayLike<Element>, message: string): void {\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n const target = block.tagName === 'CODE' ? block.parentElement : block;\n target?.classList.add('mermaid-error');\n target?.setAttribute('data-mermaid-error', message);\n }\n }\n\n /**\n * Highlight code blocks with Prism\n * Call this after the HTML has been inserted into the DOM\n * @param container The DOM element containing code blocks\n */\n public HighlightCode(container: HTMLElement): void {\n if (!this.currentConfig.enableHighlight) return;\n\n // Prism.highlightAllUnder handles finding and highlighting code blocks\n Prism.highlightAllUnder(container);\n }\n\n /** @deprecated Use {@link HighlightCode}. */\n public highlightCode(container: HTMLElement): void {\n return this.HighlightCode(container);\n }\n\n /**\n * Add copy buttons to code blocks\n * @param container The DOM element containing code blocks\n */\n public AddCodeCopyButtons(container: HTMLElement): void {\n if (!this.currentConfig.enableCodeCopy) return;\n\n const codeBlocks = container.querySelectorAll('pre > code');\n\n codeBlocks.forEach((codeBlock) => {\n const pre = codeBlock.parentElement;\n if (!pre || pre.querySelector('.code-copy-btn')) return; // Already has button\n\n // Create copy button\n const button = document.createElement('button');\n button.className = 'code-copy-btn';\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n button.title = 'Copy code';\n button.type = 'button';\n\n button.addEventListener('click', async () => {\n const code = codeBlock.textContent || '';\n\n try {\n await navigator.clipboard.writeText(code);\n button.innerHTML = '<i class=\"fas fa-check\"></i>';\n button.classList.add('copied');\n\n setTimeout(() => {\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n button.classList.remove('copied');\n }, 2000);\n } catch (err) {\n console.error('Failed to copy code:', err);\n button.innerHTML = '<i class=\"fas fa-times\"></i>';\n\n setTimeout(() => {\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n }, 2000);\n }\n });\n\n // Add toolbar wrapper\n const toolbar = document.createElement('div');\n toolbar.className = 'code-toolbar';\n toolbar.appendChild(button);\n\n // Make pre position relative for absolute positioning of toolbar\n pre.style.position = 'relative';\n pre.appendChild(toolbar);\n });\n }\n\n /** @deprecated Use {@link AddCodeCopyButtons}. */\n public addCodeCopyButtons(container: HTMLElement): void {\n return this.AddCodeCopyButtons(container);\n }\n\n /**\n * Initialize collapsible heading functionality\n * This method is a no-op - the component handles event binding\n * @param container The DOM element containing collapsible sections\n */\n public InitializeCollapsibleHeadings(_container: HTMLElement): void {\n // Event binding is handled by the component's setupCollapsibleListeners\n // This method exists for API compatibility but does nothing\n }\n\n /** @deprecated Use {@link InitializeCollapsibleHeadings}. */\n public initializeCollapsibleHeadings(_container: HTMLElement): void {\n return this.InitializeCollapsibleHeadings(_container);\n }\n\n /**\n * Get the list of headings from the last parsed document\n * Useful for building table of contents\n */\n public GetHeadingList(): HeadingInfo[] {\n return this.engine.getHeadingList();\n }\n\n /** @deprecated Use {@link GetHeadingList}. */\n public getHeadingList(): HeadingInfo[] {\n return this.GetHeadingList();\n }\n\n /**\n * Get the current configuration\n */\n public GetConfig(): ResolvedMarkdownConfig {\n return this.engine.getConfig();\n }\n\n /** @deprecated Use {@link GetConfig}. */\n public getConfig(): ResolvedMarkdownConfig {\n return this.GetConfig();\n }\n\n /**\n * Reset configuration to defaults\n */\n public ResetConfig(): void {\n this.ConfigureMarked(DEFAULT_MARKDOWN_CONFIG);\n }\n\n /** @deprecated Use {@link ResetConfig}. */\n public resetConfig(): void {\n return this.ResetConfig();\n }\n\n /**\n * Check if a language is supported by Prism\n */\n public IsLanguageSupported(lang: string): boolean {\n return !!Prism.languages[lang];\n }\n\n /** @deprecated Use {@link IsLanguageSupported}. */\n public isLanguageSupported(lang: string): boolean {\n return this.IsLanguageSupported(lang);\n }\n\n /**\n * Get list of supported Prism languages\n */\n public GetSupportedLanguages(): string[] {\n return Object.keys(Prism.languages).filter(\n lang => typeof Prism.languages[lang] === 'object'\n );\n }\n\n /** @deprecated Use {@link GetSupportedLanguages}. */\n public getSupportedLanguages(): string[] {\n return this.GetSupportedLanguages();\n }\n\n /**\n * Fix HTML that was incorrectly wrapped in <pre><code> blocks by marked.\n * This happens when marked interprets inline HTML (especially indented HTML)\n * as code blocks. We detect this by checking if the code block content\n * looks like valid HTML structure rather than actual code.\n *\n * Only processes code blocks WITHOUT a language class (e.g., language-javascript)\n * to avoid unwrapping intentional code examples.\n */\n private unwrapMiscodedHtml(html: string): string {\n // Quick check - if no pre tags, nothing to do\n if (!html.includes('<pre>')) {\n return html;\n }\n\n // Skip if SVG is present - DOMParser mangles SVG elements like <rect>\n // when parsing as 'text/html' due to namespace issues\n if (html.includes('<svg')) {\n return html;\n }\n\n try {\n const parser = new DOMParser();\n const doc = parser.parseFromString(`<div>${html}</div>`, 'text/html');\n const container = doc.body.firstChild as HTMLElement;\n\n if (!container) return html;\n\n // Find all pre > code elements WITHOUT a language class\n // Code blocks with language classes (language-javascript, etc.) are intentional\n const preElements = container.querySelectorAll('pre');\n let modified = false;\n\n for (const pre of Array.from(preElements)) {\n const code = pre.querySelector('code');\n if (!code) continue;\n\n // Skip if code has a language class - it's intentional code\n const hasLanguageClass = code.className && /language-\\w+/.test(code.className);\n if (hasLanguageClass) continue;\n\n // Get the text content (this is HTML-decoded by the browser)\n const content = code.textContent?.trim() || '';\n\n // Check if this looks like HTML that was incorrectly wrapped\n if (this.looksLikeStructuralHtml(content)) {\n // Verify it parses as valid HTML with actual elements\n const testDoc = parser.parseFromString(content, 'text/html');\n const hasStructure = testDoc.body.children.length > 0 ||\n (testDoc.body.innerHTML.trim().length > 0 &&\n testDoc.body.innerHTML.includes('<'));\n\n if (hasStructure) {\n // Replace the <pre> with the actual HTML content. The nodes are built inside a\n // <template> of the inert DOMParser document, never on the live `document`:\n // setting innerHTML on a live element fires image error handlers at parse time,\n // before the sanitizer in parse() has seen the markup. A <template> also parses\n // in body context, so a mockup that starts with <style> keeps it (a full-document\n // parse would hoist it into <head> and drop it).\n const template = doc.createElement('template');\n template.innerHTML = content;\n pre.replaceWith(template.content);\n modified = true;\n }\n }\n }\n\n if (modified) {\n return container.innerHTML;\n }\n } catch (error) {\n console.warn('Error in unwrapMiscodedHtml:', error);\n }\n\n return html;\n }\n\n /**\n * Check if content looks like structural HTML that was incorrectly\n * wrapped in a code block. We look for common HTML element patterns\n * that indicate this is meant to be rendered HTML, not code.\n */\n private looksLikeStructuralHtml(content: string): boolean {\n // Must start with < to be HTML\n if (!content.startsWith('<')) return false;\n\n // Must end with > (closing tag)\n if (!content.endsWith('>')) return false;\n\n // Check for common structural HTML tags that indicate layout HTML\n // These are tags that would typically appear in a UI mockup/prototype\n const structuralTagPattern = /<(div|span|table|tr|td|th|thead|tbody|p|ul|ol|li|section|article|header|footer|nav|main|aside|form|input|button|label|select|option|textarea|h[1-6]|img|a|strong|em|b|i|br|hr)\\b/i;\n\n if (!structuralTagPattern.test(content)) return false;\n\n // Additional check: should have multiple tags or nested structure\n // Single self-closing tags like <br> or <img> shouldn't trigger unwrapping\n const tagCount = (content.match(/<\\w+/g) || []).length;\n if (tagCount < 2) return false;\n\n // Check it's not just showing HTML as an example (common in docs)\n // If content has lots of < or > it's probably escaped HTML being shown\n if (content.includes('<') || content.includes('>')) return false;\n\n return true;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-markdown",
|
|
3
|
-
"version": "6.2.0-edge.
|
|
3
|
+
"version": "6.2.0-edge.4",
|
|
4
4
|
"description": "MemberJunction: Lightweight Angular markdown component with Prism.js highlighting, Mermaid diagrams, and extensible features",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@angular/compiler": "21.2.22",
|
|
22
22
|
"@angular/compiler-cli": "21.2.22",
|
|
23
|
-
"@memberjunction/ng-test-utils": "6.2.0-edge.
|
|
23
|
+
"@memberjunction/ng-test-utils": "6.2.0-edge.4",
|
|
24
24
|
"jsdom": "^26.1.0",
|
|
25
25
|
"vitest": "^4.0.18"
|
|
26
26
|
},
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
"@angular/platform-browser": "^21.2.22"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@memberjunction/markdown-core": "6.2.0-edge.
|
|
33
|
+
"@memberjunction/markdown-core": "6.2.0-edge.4",
|
|
34
34
|
"dompurify": "^3.3.0",
|
|
35
|
+
"katex": "^0.16.28",
|
|
35
36
|
"mermaid": "^11.12.2",
|
|
36
37
|
"prismjs": "^1.30.0",
|
|
37
38
|
"tslib": "^2.8.1"
|