@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
package/README.md
CHANGED
|
@@ -83,6 +83,7 @@ export class YourModule {}
|
|
|
83
83
|
|---------|---------|-------------|
|
|
84
84
|
| Syntax highlighting | Enabled | Prism.js with Okaidia theme |
|
|
85
85
|
| Mermaid diagrams | Enabled | Render mermaid code blocks as diagrams |
|
|
86
|
+
| Math | Enabled | TeX (`$…$` / `\(…\)` inline, `$$…$$` / `\[…\]` display), typeset as MathML by KaTeX (loaded on first use), and MathML markup, including JATS-style `<mml:math>` |
|
|
86
87
|
| Code copy button | Enabled | Copy-to-clipboard on hover |
|
|
87
88
|
| GitHub-style alerts | Enabled | `[!NOTE]`, `[!TIP]`, `[!WARNING]` blockquotes |
|
|
88
89
|
| Smartypants typography | Enabled | Curly quotes, em/en dashes, ellipsis |
|
|
@@ -98,6 +99,7 @@ export class YourModule {}
|
|
|
98
99
|
| `data` | `string` | `''` | Markdown content to render |
|
|
99
100
|
| `enableHighlight` | `boolean` | `true` | Prism.js syntax highlighting |
|
|
100
101
|
| `enableMermaid` | `boolean` | `true` | Mermaid diagram rendering |
|
|
102
|
+
| `EnableMath` | `boolean` | `true` | TeX math, and keeping markdown out of MathML markup (`<mml:math>` needs it; unprefixed `<math>` renders either way, since the sanitizer allows MathML). Single dollars follow Pandoc's rule, so `$5 and $10` stays text; `\$` is a literal dollar. PascalCase only: unlike the older inputs above, it has no lowercase alias |
|
|
101
103
|
| `enableCodeCopy` | `boolean` | `true` | Copy button on code blocks |
|
|
102
104
|
| `enableCollapsibleHeadings` | `boolean` | `false` | Collapsible heading sections |
|
|
103
105
|
| `collapsibleHeadingLevel` | `1-6` | `2` | Heading level to start collapsing |
|
|
@@ -138,6 +140,7 @@ export class YourModule {}
|
|
|
138
140
|
- `marked-smartypants` -- Typography improvements
|
|
139
141
|
- `prismjs` -- Syntax highlighting
|
|
140
142
|
- `mermaid` -- Diagram rendering
|
|
143
|
+
- `katex` -- TeX math typesetting (MathML output, so no KaTeX stylesheet or fonts are needed)
|
|
141
144
|
|
|
142
145
|
## Related Packages
|
|
143
146
|
|
|
@@ -54,6 +54,10 @@ export declare class MarkdownComponent implements OnChanges, AfterViewInit, OnDe
|
|
|
54
54
|
set enableMermaid(value: boolean);
|
|
55
55
|
/** @deprecated Use {@link EnableMermaid}. */
|
|
56
56
|
get enableMermaid(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Enable TeX math (`$…$`, `$$…$$`, `\(…\)`, `\[…\]`), typeset as MathML
|
|
59
|
+
*/
|
|
60
|
+
EnableMath: boolean;
|
|
57
61
|
/**
|
|
58
62
|
* Enable copy button on code blocks
|
|
59
63
|
*/
|
|
@@ -247,6 +251,7 @@ export declare class MarkdownComponent implements OnChanges, AfterViewInit, OnDe
|
|
|
247
251
|
get element(): ElementRef<HTMLElement>;
|
|
248
252
|
private renderStartTime;
|
|
249
253
|
private hasMermaid;
|
|
254
|
+
private hasMath;
|
|
250
255
|
private hasCodeBlocks;
|
|
251
256
|
private themeObserver;
|
|
252
257
|
constructor(elementRef: ElementRef<HTMLElement>, sanitizer: DomSanitizer, markdownService: MarkdownService, cdr: ChangeDetectorRef);
|
|
@@ -326,6 +331,6 @@ export declare class MarkdownComponent implements OnChanges, AfterViewInit, OnDe
|
|
|
326
331
|
/** @deprecated Use {@link ScrollToHeading}. */
|
|
327
332
|
scrollToHeading(headingId: string): void;
|
|
328
333
|
static ɵfac: i0.ɵɵFactoryDeclaration<MarkdownComponent, never>;
|
|
329
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<MarkdownComponent, "mj-markdown", never, { "Data": { "alias": "Data"; "required": false; }; "data": { "alias": "data"; "required": false; }; "EnableHighlight": { "alias": "EnableHighlight"; "required": false; }; "enableHighlight": { "alias": "enableHighlight"; "required": false; }; "EnableMermaid": { "alias": "EnableMermaid"; "required": false; }; "enableMermaid": { "alias": "enableMermaid"; "required": false; }; "EnableCodeCopy": { "alias": "EnableCodeCopy"; "required": false; }; "enableCodeCopy": { "alias": "enableCodeCopy"; "required": false; }; "EnableCollapsibleHeadings": { "alias": "EnableCollapsibleHeadings"; "required": false; }; "enableCollapsibleHeadings": { "alias": "enableCollapsibleHeadings"; "required": false; }; "CollapsibleHeadingLevel": { "alias": "CollapsibleHeadingLevel"; "required": false; }; "collapsibleHeadingLevel": { "alias": "collapsibleHeadingLevel"; "required": false; }; "CollapsibleDefaultExpanded": { "alias": "CollapsibleDefaultExpanded"; "required": false; }; "collapsibleDefaultExpanded": { "alias": "collapsibleDefaultExpanded"; "required": false; }; "AutoExpandLevels": { "alias": "AutoExpandLevels"; "required": false; }; "autoExpandLevels": { "alias": "autoExpandLevels"; "required": false; }; "EnableAlerts": { "alias": "EnableAlerts"; "required": false; }; "enableAlerts": { "alias": "enableAlerts"; "required": false; }; "EnableSmartypants": { "alias": "EnableSmartypants"; "required": false; }; "enableSmartypants": { "alias": "enableSmartypants"; "required": false; }; "EnableSvgRenderer": { "alias": "EnableSvgRenderer"; "required": false; }; "enableSvgRenderer": { "alias": "enableSvgRenderer"; "required": false; }; "EnableHtml": { "alias": "EnableHtml"; "required": false; }; "enableHtml": { "alias": "enableHtml"; "required": false; }; "EnableJavaScript": { "alias": "EnableJavaScript"; "required": false; }; "enableJavaScript": { "alias": "enableJavaScript"; "required": false; }; "EnableHeadingIds": { "alias": "EnableHeadingIds"; "required": false; }; "enableHeadingIds": { "alias": "enableHeadingIds"; "required": false; }; "HeadingIdPrefix": { "alias": "HeadingIdPrefix"; "required": false; }; "headingIdPrefix": { "alias": "headingIdPrefix"; "required": false; }; "EnableLineNumbers": { "alias": "EnableLineNumbers"; "required": false; }; "enableLineNumbers": { "alias": "enableLineNumbers"; "required": false; }; "ContainerClass": { "alias": "ContainerClass"; "required": false; }; "containerClass": { "alias": "containerClass"; "required": false; }; "MermaidTheme": { "alias": "MermaidTheme"; "required": false; }; "mermaidTheme": { "alias": "mermaidTheme"; "required": false; }; "Sanitize": { "alias": "Sanitize"; "required": false; }; "sanitize": { "alias": "sanitize"; "required": false; }; }, { "Rendered": "Rendered"; "rendered": "rendered"; "HeadingClick": "HeadingClick"; "headingClick": "headingClick"; "CodeCopied": "CodeCopied"; "codeCopied": "codeCopied"; }, never, never, false, never>;
|
|
334
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MarkdownComponent, "mj-markdown", never, { "Data": { "alias": "Data"; "required": false; }; "data": { "alias": "data"; "required": false; }; "EnableHighlight": { "alias": "EnableHighlight"; "required": false; }; "enableHighlight": { "alias": "enableHighlight"; "required": false; }; "EnableMermaid": { "alias": "EnableMermaid"; "required": false; }; "enableMermaid": { "alias": "enableMermaid"; "required": false; }; "EnableMath": { "alias": "EnableMath"; "required": false; }; "EnableCodeCopy": { "alias": "EnableCodeCopy"; "required": false; }; "enableCodeCopy": { "alias": "enableCodeCopy"; "required": false; }; "EnableCollapsibleHeadings": { "alias": "EnableCollapsibleHeadings"; "required": false; }; "enableCollapsibleHeadings": { "alias": "enableCollapsibleHeadings"; "required": false; }; "CollapsibleHeadingLevel": { "alias": "CollapsibleHeadingLevel"; "required": false; }; "collapsibleHeadingLevel": { "alias": "collapsibleHeadingLevel"; "required": false; }; "CollapsibleDefaultExpanded": { "alias": "CollapsibleDefaultExpanded"; "required": false; }; "collapsibleDefaultExpanded": { "alias": "collapsibleDefaultExpanded"; "required": false; }; "AutoExpandLevels": { "alias": "AutoExpandLevels"; "required": false; }; "autoExpandLevels": { "alias": "autoExpandLevels"; "required": false; }; "EnableAlerts": { "alias": "EnableAlerts"; "required": false; }; "enableAlerts": { "alias": "enableAlerts"; "required": false; }; "EnableSmartypants": { "alias": "EnableSmartypants"; "required": false; }; "enableSmartypants": { "alias": "enableSmartypants"; "required": false; }; "EnableSvgRenderer": { "alias": "EnableSvgRenderer"; "required": false; }; "enableSvgRenderer": { "alias": "enableSvgRenderer"; "required": false; }; "EnableHtml": { "alias": "EnableHtml"; "required": false; }; "enableHtml": { "alias": "enableHtml"; "required": false; }; "EnableJavaScript": { "alias": "EnableJavaScript"; "required": false; }; "enableJavaScript": { "alias": "enableJavaScript"; "required": false; }; "EnableHeadingIds": { "alias": "EnableHeadingIds"; "required": false; }; "enableHeadingIds": { "alias": "enableHeadingIds"; "required": false; }; "HeadingIdPrefix": { "alias": "HeadingIdPrefix"; "required": false; }; "headingIdPrefix": { "alias": "headingIdPrefix"; "required": false; }; "EnableLineNumbers": { "alias": "EnableLineNumbers"; "required": false; }; "enableLineNumbers": { "alias": "enableLineNumbers"; "required": false; }; "ContainerClass": { "alias": "ContainerClass"; "required": false; }; "containerClass": { "alias": "containerClass"; "required": false; }; "MermaidTheme": { "alias": "MermaidTheme"; "required": false; }; "mermaidTheme": { "alias": "mermaidTheme"; "required": false; }; "Sanitize": { "alias": "Sanitize"; "required": false; }; "sanitize": { "alias": "sanitize"; "required": false; }; }, { "Rendered": "Rendered"; "rendered": "rendered"; "HeadingClick": "HeadingClick"; "headingClick": "headingClick"; "CodeCopied": "CodeCopied"; "codeCopied": "codeCopied"; }, never, never, false, never>;
|
|
330
335
|
}
|
|
331
336
|
//# sourceMappingURL=markdown.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/markdown.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,SAAS,EACT,aAAa,EAIb,iBAAiB,EACjB,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAA2C,mBAAmB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;;AAG1H;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAQa,iBAAkB,YAAW,SAAS,EAAE,aAAa,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"markdown.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/markdown.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import { renderComponentFixture, query, capture } from '@memberjunction/ng-test-utils';
|
|
4
4
|
import { MarkdownComponent } from './markdown.component';
|
|
@@ -77,6 +77,45 @@ describe('MarkdownComponent (DOM)', () => {
|
|
|
77
77
|
expect(events.length).toBeGreaterThanOrEqual(1);
|
|
78
78
|
expect(events[events.length - 1].hasCodeBlocks).toBe(false);
|
|
79
79
|
});
|
|
80
|
+
describe('TeX math', () => {
|
|
81
|
+
// postRenderProcessing awaits the lazily-imported KaTeX chunk, so wait for the typeset
|
|
82
|
+
// output rather than for a fixed number of microtasks.
|
|
83
|
+
const typesetIn = async (f) => {
|
|
84
|
+
const c = container(f);
|
|
85
|
+
await vi.waitFor(() => expect(c.querySelector('math')).not.toBeNull());
|
|
86
|
+
return c;
|
|
87
|
+
};
|
|
88
|
+
it('typesets inline and display math after render', async () => {
|
|
89
|
+
const c = await typesetIn(render({ data: 'Area $\\pi r^2$\n\n$$\n\\sum_{i=1}^n i\n$$' }));
|
|
90
|
+
expect(c.querySelectorAll('math').length).toBe(2);
|
|
91
|
+
expect(c.querySelector('math[display="block"] munderover')).not.toBeNull();
|
|
92
|
+
expect(c.querySelector('code.mj-math')).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
it('typesets through the Angular sanitizer path too (no passthrough, no SVG renderer)', async () => {
|
|
95
|
+
const c = await typesetIn(render({ data: 'so $x_i + y_j$ holds', enableSvgRenderer: false }));
|
|
96
|
+
expect(c.querySelector('em')).toBeNull();
|
|
97
|
+
expect(c.querySelector('annotation')?.textContent).toBe('x_i + y_j');
|
|
98
|
+
});
|
|
99
|
+
it('renders a JATS MathML formula and fixes its styled letters', async () => {
|
|
100
|
+
const f = render({ data: 'Field <mml:math><mml:mi mathvariant="bold">E</mml:mi></mml:math> here' });
|
|
101
|
+
const c = container(f);
|
|
102
|
+
await vi.waitFor(() => expect(c.querySelector('mi')?.textContent).toBe('𝐄'));
|
|
103
|
+
expect(c.querySelector('math')?.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');
|
|
104
|
+
});
|
|
105
|
+
it('EnableMath=false leaves the dollars as text', async () => {
|
|
106
|
+
const f = render({ data: '$x^2$', EnableMath: false });
|
|
107
|
+
await Promise.resolve();
|
|
108
|
+
await Promise.resolve();
|
|
109
|
+
expect(container(f).querySelector('math, code.mj-math')).toBeNull();
|
|
110
|
+
expect(container(f).textContent).toContain('$x^2$');
|
|
111
|
+
});
|
|
112
|
+
it('EnableMath=false still fixes the styled letters of MathML markup', async () => {
|
|
113
|
+
const f = render({ data: 'Field <math><mi mathvariant="bold">E</mi></math> here', EnableMath: false });
|
|
114
|
+
const c = container(f);
|
|
115
|
+
await vi.waitFor(() => expect(c.querySelector('mi')?.textContent).toBe('𝐄'));
|
|
116
|
+
expect(c.querySelector('mi[mathvariant]')).toBeNull();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
80
119
|
/**
|
|
81
120
|
* Sanitization, as seen through the component. With `enableHtml` or the default
|
|
82
121
|
* `enableSvgRenderer`, Angular's sanitizer is bypassed and the HTML the service returns
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.component.dom.test.js","sourceRoot":"","sources":["../../../src/lib/components/markdown.component.dom.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport { ComponentFixture } from '@angular/core/testing';\nimport { CommonModule } from '@angular/common';\nimport { renderComponentFixture, query, capture } from '@memberjunction/ng-test-utils';\nimport { MarkdownComponent } from './markdown.component';\nimport { MarkdownRenderEvent } from '@memberjunction/markdown-core';\n\n/**\n * DOM-level spec for <mj-markdown>. The component is module-declared\n * (standalone:false), so we render it via `declarations` + `imports: [CommonModule]`\n * and configure it through `@Input`s.\n *\n * It uses the real (providedIn:'root') MarkdownService, which parses markdown\n * synchronously via marked.js. We assert on the rendered output that lands in the\n * `[innerHTML]`-bound container, plus the container-class binding and the `rendered`\n * @Output. Mermaid/Prism async post-processing is NOT asserted here (mermaid needs a\n * real browser; that's a live-test concern) — we only verify the deterministic,\n * media-free template surface.\n */\ndescribe('MarkdownComponent (DOM)', () => {\n function render(inputs: Record<string, unknown>): ComponentFixture<MarkdownComponent> {\n return renderComponentFixture(MarkdownComponent, {\n imports: [CommonModule],\n declarations: [MarkdownComponent],\n inputs,\n });\n }\n\n const container = (f: ComponentFixture<MarkdownComponent>): HTMLElement => f.nativeElement.querySelector('.mj-markdown-container') as HTMLElement;\n\n it('renders the static container element', () => {\n const f = render({ data: '' });\n expect(query(f, '.mj-markdown-container')).not.toBeNull();\n });\n\n it('renders markdown content into the innerHTML-bound container', () => {\n const f = render({ data: '# Hello World' });\n const c = container(f);\n const heading = c.querySelector('h1');\n expect(heading).not.toBeNull();\n expect(heading!.textContent).toContain('Hello World');\n });\n\n it('renders inline markdown formatting (bold) as the corresponding HTML element', () => {\n const f = render({ data: 'This is **strong** text' });\n expect(container(f).querySelector('strong')?.textContent).toBe('strong');\n });\n\n it('renders a fenced code block as a <pre><code> structure', () => {\n const f = render({ data: '```\\nconst x = 1;\\n```' });\n const c = container(f);\n expect(c.querySelector('pre')).not.toBeNull();\n expect(c.querySelector('pre code')).not.toBeNull();\n });\n\n it('renders an empty container when data is empty', () => {\n const f = render({ data: '' });\n // No data => renderedContent stays '' => no rendered child markup.\n expect(container(f).children.length).toBe(0);\n });\n\n it('applies the containerClass input to the container element', () => {\n const f = render({ data: '# X', containerClass: 'my-custom-class' });\n expect(container(f).classList.contains('my-custom-class')).toBe(true);\n });\n\n it('emits the rendered @Output with hasCodeBlocks=true for code content', async () => {\n const f = render({ data: '' });\n const events = capture<MarkdownRenderEvent>(f.componentInstance.rendered);\n\n // Setting data after first render drives ngOnChanges -> render() -> postRenderProcessing\n // is scheduled on a microtask. Use setInput (zoneless-correct), then flush the microtask.\n f.componentRef.setInput('data', '```\\nconst x = 1;\\n```');\n f.detectChanges();\n await Promise.resolve();\n await Promise.resolve();\n\n expect(events.length).toBeGreaterThanOrEqual(1);\n const last = events[events.length - 1];\n expect(last.hasCodeBlocks).toBe(true);\n expect(last.hasMermaid).toBe(false);\n });\n\n it('emits the rendered @Output with hasCodeBlocks=false for prose-only content', async () => {\n const f = render({ data: '' });\n const events = capture<MarkdownRenderEvent>(f.componentInstance.rendered);\n\n f.componentRef.setInput('data', 'Just some plain prose.');\n f.detectChanges();\n await Promise.resolve();\n await Promise.resolve();\n\n expect(events.length).toBeGreaterThanOrEqual(1);\n expect(events[events.length - 1].hasCodeBlocks).toBe(false);\n });\n\n /**\n * Sanitization, as seen through the component. With `enableHtml` or the default\n * `enableSvgRenderer`, Angular's sanitizer is bypassed and the HTML the service returns\n * (sanitized by DOMPurify in parse()) is trusted. jsdom does not execute handlers or\n * load images, so assertions are structural: the vector is gone, the layout survives.\n */\n describe('sanitization', () => {\n const attributeNamesIn = (root: HTMLElement): string[] =>\n Array.from(root.querySelectorAll('*')).flatMap((el) => Array.from(el.attributes).map((a) => a.name.toLowerCase()));\n const passthrough = (data: string) => container(render({ data, enableHtml: true }));\n\n it('removes <script> elements, including ones with a spaced end tag', () => {\n const c = passthrough('<div>before</div><script>alert(1)</script ><div>after</div>');\n expect(c.querySelector('script')).toBeNull();\n expect(c.textContent).toContain('before');\n expect(c.textContent).toContain('after');\n expect(c.textContent).not.toContain('alert(1)');\n });\n\n it('removes every on* handler, whatever its name or quoting', () => {\n const c = passthrough('<img src=\"x\" onerror=\"alert(1)\"><div onpointerrawupdate=alert(1) ONMOUSEENTER=\"alert(1)\">t</div><svg onload=\"alert(1)\"><circle r=\"1\"/></svg>');\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n expect(c.querySelector('img')).not.toBeNull();\n expect(c.querySelector('svg circle')).not.toBeNull();\n });\n\n it.each([\n ['javascript:', '<a href=\"javascript:alert(1)\">x</a>'],\n ['vbscript:', '<a href=\"vbscript:msgbox(1)\">x</a>'],\n ['data:text/html', '<a href=\"data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==\">x</a>'],\n ['data:image/svg+xml on a link', '<a href=\"data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9YWxlcnQoMSk+PC9zdmc+\">x</a>'],\n ['whitespace inside the scheme', '<a href=\"java\\tscript:alert(1)\">x</a>'],\n ['entity inside the scheme', '<a href=\"jav	ascript:alert(1)\">x</a>'],\n ['newline inside the scheme', '<a href=\"java\\nscript:alert(1)\">x</a>'],\n ['upper-case scheme', '<a href=\"JAVASCRIPT:alert(1)\">x</a>'],\n ])('drops the link target for %s', (_label, html) => {\n const a = passthrough(html).querySelector('a');\n expect(a).not.toBeNull();\n expect(a?.hasAttribute('href')).toBe(false);\n });\n\n it('drops javascript: from a form action', () => {\n const c = passthrough('<form action=\"javascript:alert(1)\"><button>go</button></form>');\n expect(c.querySelector('form')?.hasAttribute('action')).toBe(false);\n });\n\n it.each([\n ['iframe', '<iframe src=\"https://evil.test/\"></iframe>'],\n ['iframe with srcdoc', '<iframe srcdoc=\"<script>alert(1)</script>\"></iframe>'],\n ['object', '<object data=\"https://evil.test/x.swf\"></object>'],\n ['embed', '<embed src=\"https://evil.test/x.swf\">'],\n ['base', '<base href=\"https://evil.test/\">'],\n ])('removes %s while keeping the surrounding content', (_label, html) => {\n const c = passthrough('<p>before</p>' + html + '<p>after</p>');\n expect(c.querySelector('iframe, object, embed, base')).toBeNull();\n expect(c.textContent).toContain('before');\n expect(c.textContent).toContain('after');\n });\n\n it('removes <foreignObject> and SMIL animation from inline SVG, keeps shapes', () => {\n const c = passthrough('<svg><foreignObject><body onload=\"alert(1)\"></body></foreignObject><circle r=\"1\"><animate attributeName=\"r\" to=\"2\"/></circle></svg>');\n expect(c.querySelector('foreignObject, animate')).toBeNull();\n expect(c.querySelector('svg circle')).not.toBeNull();\n });\n\n it('keeps a same-document <use> and removes an external one', () => {\n const c = passthrough('<svg><defs><symbol id=\"ic\"><circle r=\"1\"/></symbol></defs><use href=\"#ic\"/><use href=\"https://evil.test/s.svg#x\"/></svg>');\n const uses = Array.from(c.querySelectorAll('use')).map((u) => u.getAttribute('href'));\n expect(uses).toEqual(['#ic']);\n });\n\n it('keeps layout HTML, inline styles, data attributes and data: images', () => {\n const c = passthrough(\n '<table class=\"grid\" data-role=\"report\"><tr><td style=\"color: red\">cell</td></tr></table>' +\n '<input type=\"text\" value=\"v\"><details><summary>s</summary>body</details>' +\n '<img src=\"data:image/png;base64,iVBORw0KGgo=\" alt=\"pixel\">',\n );\n expect(c.querySelector('table.grid')?.getAttribute('data-role')).toBe('report');\n expect(c.querySelector('td')?.getAttribute('style')).toContain('color');\n expect(c.querySelector('input')?.getAttribute('value')).toBe('v');\n expect(c.querySelector('details summary')?.textContent).toBe('s');\n expect(c.querySelector('img')?.getAttribute('src')).toMatch(/^data:image\\/png/);\n });\n\n it('keeps a leading <style> block (mockups start that way)', () => {\n const c = passthrough('<style>.card{color:red}</style><div class=\"card\">x</div>');\n expect(c.querySelector('style')?.textContent).toContain('.card');\n expect(c.querySelector('div.card')).not.toBeNull();\n });\n\n it('keeps target on links and enforces rel=noopener', () => {\n const c = passthrough('<a href=\"https://example.test/page\" target=\"_blank\">site</a><a href=\"#section\">local</a>');\n const [ext, local] = Array.from(c.querySelectorAll('a'));\n expect(ext.getAttribute('href')).toBe('https://example.test/page');\n expect(ext.getAttribute('target')).toBe('_blank');\n expect(ext.getAttribute('rel')).toContain('noopener');\n expect(local.getAttribute('href')).toBe('#section');\n expect(local.hasAttribute('rel')).toBe(false);\n });\n\n it('keeps heading ids that collide with document properties', () => {\n // DOMPurify's default DOM-clobbering guard would strip these; heading anchors must work.\n const c = container(render({ data: '# Title' + '\\n\\n' + '## Location' + '\\n\\n' + '## Overview' }));\n expect(c.querySelector('h1')?.id).toBe('title');\n expect(c.querySelector('h2')?.id).toBe('location');\n expect(Array.from(c.querySelectorAll('h2')).map((h) => h.id)).toEqual(['location', 'overview']);\n });\n\n it('sanitizes HTML that marked miscoded into a code block and the service unwrapped', () => {\n // A bare fenced block whose text is structural HTML is unwrapped by the service (no\n // language class), then sanitized like any other passthrough markup.\n const c = passthrough('```' + '\\n' + '<div class=\"card\"><img src=\"x\" onerror=\"alert(1)\"><p>inner</p></div>' + '\\n' + '```');\n expect(c.querySelector('pre')).toBeNull();\n expect(c.querySelector('div.card p')?.textContent).toBe('inner');\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n });\n\n it('strips handlers and script from a rendered SVG code fence (enableSvgRenderer)', () => {\n const data = '```svg' + '\\n' + '<svg xmlns=\"http://www.w3.org/2000/svg\" onload=\"alert(1)\"><script>alert(2)</script><circle r=\"1\"/></svg>' + '\\n' + '```';\n const c = container(render({ data, enableSvgRenderer: true }));\n expect(c.querySelector('svg')).not.toBeNull();\n expect(c.querySelector('script')).toBeNull();\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n });\n\n it('enableJavaScript=true is the documented opt-out and leaves script elements in place', () => {\n // jsdom (as vitest configures it) executes this script and logs \"Not implemented:\n // window.alert\" to stderr. Expected here and only here.\n const c = container(render({ data: '<div>x</div><script>alert(1)</script>', enableHtml: true, enableJavaScript: true }));\n expect(c.querySelector('script')).not.toBeNull();\n });\n\n it('Angular sanitizer path (no passthrough, no SVG renderer) still strips handlers', () => {\n const c = container(render({ data: 'text <img src=\"x\" onerror=\"alert(1)\">', enableSvgRenderer: false }));\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n });\n });\n});\n"]}
|
|
1
|
+
{"version":3,"file":"markdown.component.dom.test.js","sourceRoot":"","sources":["../../../src/lib/components/markdown.component.dom.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, vi } from 'vitest';\nimport { ComponentFixture } from '@angular/core/testing';\nimport { CommonModule } from '@angular/common';\nimport { renderComponentFixture, query, capture } from '@memberjunction/ng-test-utils';\nimport { MarkdownComponent } from './markdown.component';\nimport { MarkdownRenderEvent } from '@memberjunction/markdown-core';\n\n/**\n * DOM-level spec for <mj-markdown>. The component is module-declared\n * (standalone:false), so we render it via `declarations` + `imports: [CommonModule]`\n * and configure it through `@Input`s.\n *\n * It uses the real (providedIn:'root') MarkdownService, which parses markdown\n * synchronously via marked.js. We assert on the rendered output that lands in the\n * `[innerHTML]`-bound container, plus the container-class binding and the `rendered`\n * @Output. Mermaid/Prism async post-processing is NOT asserted here (mermaid needs a\n * real browser; that's a live-test concern) — we only verify the deterministic,\n * media-free template surface.\n */\ndescribe('MarkdownComponent (DOM)', () => {\n function render(inputs: Record<string, unknown>): ComponentFixture<MarkdownComponent> {\n return renderComponentFixture(MarkdownComponent, {\n imports: [CommonModule],\n declarations: [MarkdownComponent],\n inputs,\n });\n }\n\n const container = (f: ComponentFixture<MarkdownComponent>): HTMLElement => f.nativeElement.querySelector('.mj-markdown-container') as HTMLElement;\n\n it('renders the static container element', () => {\n const f = render({ data: '' });\n expect(query(f, '.mj-markdown-container')).not.toBeNull();\n });\n\n it('renders markdown content into the innerHTML-bound container', () => {\n const f = render({ data: '# Hello World' });\n const c = container(f);\n const heading = c.querySelector('h1');\n expect(heading).not.toBeNull();\n expect(heading!.textContent).toContain('Hello World');\n });\n\n it('renders inline markdown formatting (bold) as the corresponding HTML element', () => {\n const f = render({ data: 'This is **strong** text' });\n expect(container(f).querySelector('strong')?.textContent).toBe('strong');\n });\n\n it('renders a fenced code block as a <pre><code> structure', () => {\n const f = render({ data: '```\\nconst x = 1;\\n```' });\n const c = container(f);\n expect(c.querySelector('pre')).not.toBeNull();\n expect(c.querySelector('pre code')).not.toBeNull();\n });\n\n it('renders an empty container when data is empty', () => {\n const f = render({ data: '' });\n // No data => renderedContent stays '' => no rendered child markup.\n expect(container(f).children.length).toBe(0);\n });\n\n it('applies the containerClass input to the container element', () => {\n const f = render({ data: '# X', containerClass: 'my-custom-class' });\n expect(container(f).classList.contains('my-custom-class')).toBe(true);\n });\n\n it('emits the rendered @Output with hasCodeBlocks=true for code content', async () => {\n const f = render({ data: '' });\n const events = capture<MarkdownRenderEvent>(f.componentInstance.rendered);\n\n // Setting data after first render drives ngOnChanges -> render() -> postRenderProcessing\n // is scheduled on a microtask. Use setInput (zoneless-correct), then flush the microtask.\n f.componentRef.setInput('data', '```\\nconst x = 1;\\n```');\n f.detectChanges();\n await Promise.resolve();\n await Promise.resolve();\n\n expect(events.length).toBeGreaterThanOrEqual(1);\n const last = events[events.length - 1];\n expect(last.hasCodeBlocks).toBe(true);\n expect(last.hasMermaid).toBe(false);\n });\n\n it('emits the rendered @Output with hasCodeBlocks=false for prose-only content', async () => {\n const f = render({ data: '' });\n const events = capture<MarkdownRenderEvent>(f.componentInstance.rendered);\n\n f.componentRef.setInput('data', 'Just some plain prose.');\n f.detectChanges();\n await Promise.resolve();\n await Promise.resolve();\n\n expect(events.length).toBeGreaterThanOrEqual(1);\n expect(events[events.length - 1].hasCodeBlocks).toBe(false);\n });\n\n describe('TeX math', () => {\n // postRenderProcessing awaits the lazily-imported KaTeX chunk, so wait for the typeset\n // output rather than for a fixed number of microtasks.\n const typesetIn = async (f: ComponentFixture<MarkdownComponent>): Promise<HTMLElement> => {\n const c = container(f);\n await vi.waitFor(() => expect(c.querySelector('math')).not.toBeNull());\n return c;\n };\n\n it('typesets inline and display math after render', async () => {\n const c = await typesetIn(render({ data: 'Area $\\\\pi r^2$\\n\\n$$\\n\\\\sum_{i=1}^n i\\n$$' }));\n expect(c.querySelectorAll('math').length).toBe(2);\n expect(c.querySelector('math[display=\"block\"] munderover')).not.toBeNull();\n expect(c.querySelector('code.mj-math')).toBeNull();\n });\n\n it('typesets through the Angular sanitizer path too (no passthrough, no SVG renderer)', async () => {\n const c = await typesetIn(render({ data: 'so $x_i + y_j$ holds', enableSvgRenderer: false }));\n expect(c.querySelector('em')).toBeNull();\n expect(c.querySelector('annotation')?.textContent).toBe('x_i + y_j');\n });\n\n it('renders a JATS MathML formula and fixes its styled letters', async () => {\n const f = render({ data: 'Field <mml:math><mml:mi mathvariant=\"bold\">E</mml:mi></mml:math> here' });\n const c = container(f);\n await vi.waitFor(() => expect(c.querySelector('mi')?.textContent).toBe('𝐄'));\n expect(c.querySelector('math')?.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');\n });\n\n it('EnableMath=false leaves the dollars as text', async () => {\n const f = render({ data: '$x^2$', EnableMath: false });\n await Promise.resolve();\n await Promise.resolve();\n expect(container(f).querySelector('math, code.mj-math')).toBeNull();\n expect(container(f).textContent).toContain('$x^2$');\n });\n\n it('EnableMath=false still fixes the styled letters of MathML markup', async () => {\n const f = render({ data: 'Field <math><mi mathvariant=\"bold\">E</mi></math> here', EnableMath: false });\n const c = container(f);\n await vi.waitFor(() => expect(c.querySelector('mi')?.textContent).toBe('𝐄'));\n expect(c.querySelector('mi[mathvariant]')).toBeNull();\n });\n });\n\n /**\n * Sanitization, as seen through the component. With `enableHtml` or the default\n * `enableSvgRenderer`, Angular's sanitizer is bypassed and the HTML the service returns\n * (sanitized by DOMPurify in parse()) is trusted. jsdom does not execute handlers or\n * load images, so assertions are structural: the vector is gone, the layout survives.\n */\n describe('sanitization', () => {\n const attributeNamesIn = (root: HTMLElement): string[] =>\n Array.from(root.querySelectorAll('*')).flatMap((el) => Array.from(el.attributes).map((a) => a.name.toLowerCase()));\n const passthrough = (data: string) => container(render({ data, enableHtml: true }));\n\n it('removes <script> elements, including ones with a spaced end tag', () => {\n const c = passthrough('<div>before</div><script>alert(1)</script ><div>after</div>');\n expect(c.querySelector('script')).toBeNull();\n expect(c.textContent).toContain('before');\n expect(c.textContent).toContain('after');\n expect(c.textContent).not.toContain('alert(1)');\n });\n\n it('removes every on* handler, whatever its name or quoting', () => {\n const c = passthrough('<img src=\"x\" onerror=\"alert(1)\"><div onpointerrawupdate=alert(1) ONMOUSEENTER=\"alert(1)\">t</div><svg onload=\"alert(1)\"><circle r=\"1\"/></svg>');\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n expect(c.querySelector('img')).not.toBeNull();\n expect(c.querySelector('svg circle')).not.toBeNull();\n });\n\n it.each([\n ['javascript:', '<a href=\"javascript:alert(1)\">x</a>'],\n ['vbscript:', '<a href=\"vbscript:msgbox(1)\">x</a>'],\n ['data:text/html', '<a href=\"data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==\">x</a>'],\n ['data:image/svg+xml on a link', '<a href=\"data:image/svg+xml;base64,PHN2ZyBvbmxvYWQ9YWxlcnQoMSk+PC9zdmc+\">x</a>'],\n ['whitespace inside the scheme', '<a href=\"java\\tscript:alert(1)\">x</a>'],\n ['entity inside the scheme', '<a href=\"jav	ascript:alert(1)\">x</a>'],\n ['newline inside the scheme', '<a href=\"java\\nscript:alert(1)\">x</a>'],\n ['upper-case scheme', '<a href=\"JAVASCRIPT:alert(1)\">x</a>'],\n ])('drops the link target for %s', (_label, html) => {\n const a = passthrough(html).querySelector('a');\n expect(a).not.toBeNull();\n expect(a?.hasAttribute('href')).toBe(false);\n });\n\n it('drops javascript: from a form action', () => {\n const c = passthrough('<form action=\"javascript:alert(1)\"><button>go</button></form>');\n expect(c.querySelector('form')?.hasAttribute('action')).toBe(false);\n });\n\n it.each([\n ['iframe', '<iframe src=\"https://evil.test/\"></iframe>'],\n ['iframe with srcdoc', '<iframe srcdoc=\"<script>alert(1)</script>\"></iframe>'],\n ['object', '<object data=\"https://evil.test/x.swf\"></object>'],\n ['embed', '<embed src=\"https://evil.test/x.swf\">'],\n ['base', '<base href=\"https://evil.test/\">'],\n ])('removes %s while keeping the surrounding content', (_label, html) => {\n const c = passthrough('<p>before</p>' + html + '<p>after</p>');\n expect(c.querySelector('iframe, object, embed, base')).toBeNull();\n expect(c.textContent).toContain('before');\n expect(c.textContent).toContain('after');\n });\n\n it('removes <foreignObject> and SMIL animation from inline SVG, keeps shapes', () => {\n const c = passthrough('<svg><foreignObject><body onload=\"alert(1)\"></body></foreignObject><circle r=\"1\"><animate attributeName=\"r\" to=\"2\"/></circle></svg>');\n expect(c.querySelector('foreignObject, animate')).toBeNull();\n expect(c.querySelector('svg circle')).not.toBeNull();\n });\n\n it('keeps a same-document <use> and removes an external one', () => {\n const c = passthrough('<svg><defs><symbol id=\"ic\"><circle r=\"1\"/></symbol></defs><use href=\"#ic\"/><use href=\"https://evil.test/s.svg#x\"/></svg>');\n const uses = Array.from(c.querySelectorAll('use')).map((u) => u.getAttribute('href'));\n expect(uses).toEqual(['#ic']);\n });\n\n it('keeps layout HTML, inline styles, data attributes and data: images', () => {\n const c = passthrough(\n '<table class=\"grid\" data-role=\"report\"><tr><td style=\"color: red\">cell</td></tr></table>' +\n '<input type=\"text\" value=\"v\"><details><summary>s</summary>body</details>' +\n '<img src=\"data:image/png;base64,iVBORw0KGgo=\" alt=\"pixel\">',\n );\n expect(c.querySelector('table.grid')?.getAttribute('data-role')).toBe('report');\n expect(c.querySelector('td')?.getAttribute('style')).toContain('color');\n expect(c.querySelector('input')?.getAttribute('value')).toBe('v');\n expect(c.querySelector('details summary')?.textContent).toBe('s');\n expect(c.querySelector('img')?.getAttribute('src')).toMatch(/^data:image\\/png/);\n });\n\n it('keeps a leading <style> block (mockups start that way)', () => {\n const c = passthrough('<style>.card{color:red}</style><div class=\"card\">x</div>');\n expect(c.querySelector('style')?.textContent).toContain('.card');\n expect(c.querySelector('div.card')).not.toBeNull();\n });\n\n it('keeps target on links and enforces rel=noopener', () => {\n const c = passthrough('<a href=\"https://example.test/page\" target=\"_blank\">site</a><a href=\"#section\">local</a>');\n const [ext, local] = Array.from(c.querySelectorAll('a'));\n expect(ext.getAttribute('href')).toBe('https://example.test/page');\n expect(ext.getAttribute('target')).toBe('_blank');\n expect(ext.getAttribute('rel')).toContain('noopener');\n expect(local.getAttribute('href')).toBe('#section');\n expect(local.hasAttribute('rel')).toBe(false);\n });\n\n it('keeps heading ids that collide with document properties', () => {\n // DOMPurify's default DOM-clobbering guard would strip these; heading anchors must work.\n const c = container(render({ data: '# Title' + '\\n\\n' + '## Location' + '\\n\\n' + '## Overview' }));\n expect(c.querySelector('h1')?.id).toBe('title');\n expect(c.querySelector('h2')?.id).toBe('location');\n expect(Array.from(c.querySelectorAll('h2')).map((h) => h.id)).toEqual(['location', 'overview']);\n });\n\n it('sanitizes HTML that marked miscoded into a code block and the service unwrapped', () => {\n // A bare fenced block whose text is structural HTML is unwrapped by the service (no\n // language class), then sanitized like any other passthrough markup.\n const c = passthrough('```' + '\\n' + '<div class=\"card\"><img src=\"x\" onerror=\"alert(1)\"><p>inner</p></div>' + '\\n' + '```');\n expect(c.querySelector('pre')).toBeNull();\n expect(c.querySelector('div.card p')?.textContent).toBe('inner');\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n });\n\n it('strips handlers and script from a rendered SVG code fence (enableSvgRenderer)', () => {\n const data = '```svg' + '\\n' + '<svg xmlns=\"http://www.w3.org/2000/svg\" onload=\"alert(1)\"><script>alert(2)</script><circle r=\"1\"/></svg>' + '\\n' + '```';\n const c = container(render({ data, enableSvgRenderer: true }));\n expect(c.querySelector('svg')).not.toBeNull();\n expect(c.querySelector('script')).toBeNull();\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n });\n\n it('enableJavaScript=true is the documented opt-out and leaves script elements in place', () => {\n // jsdom (as vitest configures it) executes this script and logs \"Not implemented:\n // window.alert\" to stderr. Expected here and only here.\n const c = container(render({ data: '<div>x</div><script>alert(1)</script>', enableHtml: true, enableJavaScript: true }));\n expect(c.querySelector('script')).not.toBeNull();\n });\n\n it('Angular sanitizer path (no passthrough, no SVG renderer) still strips handlers', () => {\n const c = container(render({ data: 'text <img src=\"x\" onerror=\"alert(1)\">', enableSvgRenderer: false }));\n expect(attributeNamesIn(c).some((n) => n.startsWith('on'))).toBe(false);\n });\n });\n});\n"]}
|
|
@@ -217,6 +217,10 @@ export class MarkdownComponent {
|
|
|
217
217
|
* Enable Mermaid diagram rendering
|
|
218
218
|
*/
|
|
219
219
|
this.EnableMermaid = DEFAULT_MARKDOWN_CONFIG.enableMermaid;
|
|
220
|
+
/**
|
|
221
|
+
* Enable TeX math (`$…$`, `$$…$$`, `\(…\)`, `\[…\]`), typeset as MathML
|
|
222
|
+
*/
|
|
223
|
+
this.EnableMath = DEFAULT_MARKDOWN_CONFIG.enableMath;
|
|
220
224
|
/**
|
|
221
225
|
* Enable copy button on code blocks
|
|
222
226
|
*/
|
|
@@ -323,6 +327,7 @@ export class MarkdownComponent {
|
|
|
323
327
|
this.RenderedContent = '';
|
|
324
328
|
this.renderStartTime = 0;
|
|
325
329
|
this.hasMermaid = false;
|
|
330
|
+
this.hasMath = false;
|
|
326
331
|
this.hasCodeBlocks = false;
|
|
327
332
|
this.themeObserver = null;
|
|
328
333
|
}
|
|
@@ -331,6 +336,7 @@ export class MarkdownComponent {
|
|
|
331
336
|
const needsRerender = changes['data'] ||
|
|
332
337
|
changes['enableHighlight'] ||
|
|
333
338
|
changes['enableMermaid'] ||
|
|
339
|
+
changes['EnableMath'] ||
|
|
334
340
|
changes['enableCodeCopy'] ||
|
|
335
341
|
changes['enableCollapsibleHeadings'] ||
|
|
336
342
|
changes['collapsibleHeadingLevel'] ||
|
|
@@ -375,6 +381,7 @@ export class MarkdownComponent {
|
|
|
375
381
|
const config = {
|
|
376
382
|
enableHighlight: this.EnableHighlight,
|
|
377
383
|
enableMermaid: this.EnableMermaid,
|
|
384
|
+
enableMath: this.EnableMath,
|
|
378
385
|
enableCodeCopy: this.EnableCodeCopy,
|
|
379
386
|
enableCollapsibleHeadings: this.EnableCollapsibleHeadings,
|
|
380
387
|
collapsibleHeadingLevel: this.CollapsibleHeadingLevel,
|
|
@@ -396,6 +403,7 @@ export class MarkdownComponent {
|
|
|
396
403
|
// Check for mermaid and code blocks
|
|
397
404
|
this.hasMermaid = html.includes('language-mermaid') || html.includes('class="mermaid"');
|
|
398
405
|
this.hasCodeBlocks = html.includes('<pre>') && html.includes('<code');
|
|
406
|
+
this.hasMath = html.includes('class="mj-math') || html.includes('<math');
|
|
399
407
|
// Sanitize if enabled
|
|
400
408
|
// Note: We bypass Angular's sanitizer when SVG renderer or HTML passthrough is enabled
|
|
401
409
|
// because it strips SVG elements and most HTML layout tags.
|
|
@@ -405,7 +413,7 @@ export class MarkdownComponent {
|
|
|
405
413
|
html = sanitized || '';
|
|
406
414
|
}
|
|
407
415
|
// When Angular's sanitizer is bypassed, the HTML has already been sanitized by
|
|
408
|
-
// MarkdownService.parse() (DOMPurify, HTML
|
|
416
|
+
// MarkdownService.parse() (DOMPurify, HTML, SVG and MathML profiles) unless enableJavaScript
|
|
409
417
|
// opted out. Nothing further to do here.
|
|
410
418
|
// Trust the HTML for display
|
|
411
419
|
this.RenderedContent = this.sanitizer.bypassSecurityTrustHtml(html);
|
|
@@ -430,6 +438,16 @@ export class MarkdownComponent {
|
|
|
430
438
|
this.markdownService.initializeCollapsibleHeadings(container);
|
|
431
439
|
this.setupCollapsibleListeners(container);
|
|
432
440
|
}
|
|
441
|
+
// Typeset TeX math (async: KaTeX loads on first use). With math off, MathML that arrived as
|
|
442
|
+
// markup still renders, so it still gets the styled-letter fix.
|
|
443
|
+
if (this.hasMath) {
|
|
444
|
+
if (this.EnableMath) {
|
|
445
|
+
await this.markdownService.RenderMath(container);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
this.markdownService.UseUnicodeMathLetters(container);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
433
451
|
// Render mermaid diagrams (async)
|
|
434
452
|
if (this.EnableMermaid && this.hasMermaid) {
|
|
435
453
|
await this.markdownService.renderMermaid(container);
|
|
@@ -703,16 +721,16 @@ export class MarkdownComponent {
|
|
|
703
721
|
return this.ScrollToHeading(headingId);
|
|
704
722
|
}
|
|
705
723
|
static { this.ɵfac = function MarkdownComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || MarkdownComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.DomSanitizer), i0.ɵɵdirectiveInject(i2.MarkdownService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
|
|
706
|
-
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MarkdownComponent, selectors: [["mj-markdown"]], inputs: { Data: "Data", data: "data", EnableHighlight: "EnableHighlight", enableHighlight: "enableHighlight", EnableMermaid: "EnableMermaid", enableMermaid: "enableMermaid", EnableCodeCopy: "EnableCodeCopy", enableCodeCopy: "enableCodeCopy", EnableCollapsibleHeadings: "EnableCollapsibleHeadings", enableCollapsibleHeadings: "enableCollapsibleHeadings", CollapsibleHeadingLevel: "CollapsibleHeadingLevel", collapsibleHeadingLevel: "collapsibleHeadingLevel", CollapsibleDefaultExpanded: "CollapsibleDefaultExpanded", collapsibleDefaultExpanded: "collapsibleDefaultExpanded", AutoExpandLevels: "AutoExpandLevels", autoExpandLevels: "autoExpandLevels", EnableAlerts: "EnableAlerts", enableAlerts: "enableAlerts", EnableSmartypants: "EnableSmartypants", enableSmartypants: "enableSmartypants", EnableSvgRenderer: "EnableSvgRenderer", enableSvgRenderer: "enableSvgRenderer", EnableHtml: "EnableHtml", enableHtml: "enableHtml", EnableJavaScript: "EnableJavaScript", enableJavaScript: "enableJavaScript", EnableHeadingIds: "EnableHeadingIds", enableHeadingIds: "enableHeadingIds", HeadingIdPrefix: "HeadingIdPrefix", headingIdPrefix: "headingIdPrefix", EnableLineNumbers: "EnableLineNumbers", enableLineNumbers: "enableLineNumbers", ContainerClass: "ContainerClass", containerClass: "containerClass", MermaidTheme: "MermaidTheme", mermaidTheme: "mermaidTheme", Sanitize: "Sanitize", sanitize: "sanitize" }, outputs: { Rendered: "Rendered", rendered: "rendered", HeadingClick: "HeadingClick", headingClick: "headingClick", CodeCopied: "CodeCopied", codeCopied: "codeCopied" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 1, vars: 3, consts: [[1, "mj-markdown-container", 3, "innerHTML"]], template: function MarkdownComponent_Template(rf, ctx) { if (rf & 1) {
|
|
724
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MarkdownComponent, selectors: [["mj-markdown"]], inputs: { Data: "Data", data: "data", EnableHighlight: "EnableHighlight", enableHighlight: "enableHighlight", EnableMermaid: "EnableMermaid", enableMermaid: "enableMermaid", EnableMath: "EnableMath", EnableCodeCopy: "EnableCodeCopy", enableCodeCopy: "enableCodeCopy", EnableCollapsibleHeadings: "EnableCollapsibleHeadings", enableCollapsibleHeadings: "enableCollapsibleHeadings", CollapsibleHeadingLevel: "CollapsibleHeadingLevel", collapsibleHeadingLevel: "collapsibleHeadingLevel", CollapsibleDefaultExpanded: "CollapsibleDefaultExpanded", collapsibleDefaultExpanded: "collapsibleDefaultExpanded", AutoExpandLevels: "AutoExpandLevels", autoExpandLevels: "autoExpandLevels", EnableAlerts: "EnableAlerts", enableAlerts: "enableAlerts", EnableSmartypants: "EnableSmartypants", enableSmartypants: "enableSmartypants", EnableSvgRenderer: "EnableSvgRenderer", enableSvgRenderer: "enableSvgRenderer", EnableHtml: "EnableHtml", enableHtml: "enableHtml", EnableJavaScript: "EnableJavaScript", enableJavaScript: "enableJavaScript", EnableHeadingIds: "EnableHeadingIds", enableHeadingIds: "enableHeadingIds", HeadingIdPrefix: "HeadingIdPrefix", headingIdPrefix: "headingIdPrefix", EnableLineNumbers: "EnableLineNumbers", enableLineNumbers: "enableLineNumbers", ContainerClass: "ContainerClass", containerClass: "containerClass", MermaidTheme: "MermaidTheme", mermaidTheme: "mermaidTheme", Sanitize: "Sanitize", sanitize: "sanitize" }, outputs: { Rendered: "Rendered", rendered: "rendered", HeadingClick: "HeadingClick", headingClick: "headingClick", CodeCopied: "CodeCopied", codeCopied: "codeCopied" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 1, vars: 3, consts: [[1, "mj-markdown-container", 3, "innerHTML"]], template: function MarkdownComponent_Template(rf, ctx) { if (rf & 1) {
|
|
707
725
|
i0.ɵɵelement(0, "div", 0);
|
|
708
726
|
} if (rf & 2) {
|
|
709
727
|
i0.ɵɵclassMap(ctx.containerClass);
|
|
710
728
|
i0.ɵɵproperty("innerHTML", ctx.renderedContent, i0.ɵɵsanitizeHtml);
|
|
711
|
-
} }, styles: ["/**\n * MJ Markdown Component Styles\n *\n * These styles apply to rendered markdown content.\n * Using ViewEncapsulation.None so styles penetrate into dynamically generated content.\n */\n\n/* ============================================\n Container\n ============================================ */\n.mj-markdown-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 15px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n word-wrap: break-word;\n}\n\n/* ============================================\n Typography\n ============================================ */\n.mj-markdown-container h1,\n.mj-markdown-container h2,\n.mj-markdown-container h3,\n.mj-markdown-container h4,\n.mj-markdown-container h5,\n.mj-markdown-container h6 {\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.25;\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container h1 { font-size: 1.75em; }\n.mj-markdown-container h2 { font-size: 1.5em; }\n.mj-markdown-container h3 { font-size: 1.25em; }\n.mj-markdown-container h4 { font-size: 1.1em; }\n.mj-markdown-container h5 { font-size: 1em; }\n.mj-markdown-container h6 { font-size: 0.9em; color: var(--mj-text-secondary); }\n\n.mj-markdown-container > h1:first-child,\n.mj-markdown-container > h2:first-child,\n.mj-markdown-container > h3:first-child {\n margin-top: 0;\n}\n\n.mj-markdown-container p {\n margin: 0 0 1em 0;\n}\n\n.mj-markdown-container > p:last-child {\n margin-bottom: 0;\n}\n\n.mj-markdown-container a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n}\n\n.mj-markdown-container a:hover {\n text-decoration: underline;\n}\n\n.mj-markdown-container strong {\n font-weight: 600;\n}\n\n.mj-markdown-container em {\n font-style: italic;\n}\n\n/* ============================================\n Lists\n ============================================ */\n.mj-markdown-container ul,\n.mj-markdown-container ol {\n margin: 0 0 1em 0;\n padding-left: 2em;\n}\n\n.mj-markdown-container li {\n margin-bottom: 0.25em;\n}\n\n.mj-markdown-container li > ul,\n.mj-markdown-container li > ol {\n margin-top: 0.25em;\n margin-bottom: 0;\n}\n\n/* Task lists */\n.mj-markdown-container ul.contains-task-list {\n list-style: none;\n padding-left: 0;\n}\n\n.mj-markdown-container li.task-list-item {\n display: flex;\n align-items: flex-start;\n gap: 0.5em;\n}\n\n.mj-markdown-container li.task-list-item input[type=\"checkbox\"] {\n margin-top: 0.35em;\n}\n\n/* ============================================\n Blockquotes\n ============================================ */\n.mj-markdown-container blockquote {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 4px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.mj-markdown-container blockquote > p:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================\n Code Blocks\n ============================================ */\n.mj-markdown-container code {\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;\n font-size: 0.9em;\n}\n\n/* Inline code */\n.mj-markdown-container :not(pre) > code {\n padding: 0.2em 0.4em;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n color: var(--mj-status-error);\n}\n\n/* Code blocks\n * design-token-exempt fallbacks: --mj-bg-code-block / --mj-text-code resolve to\n * Monaco-style dark editor colors when an app does not override them. Code\n * blocks intentionally use a fixed dark editor aesthetic (same exemption as\n * Monaco/CodeMirror), so the hex fallbacks here are deliberate. */\n.mj-markdown-container pre {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-code-block, #1e1e1e);\n border-radius: var(--mj-radius-md);\n overflow-x: auto;\n position: relative;\n}\n\n.mj-markdown-container pre code {\n background: transparent;\n padding: 0;\n color: var(--mj-text-code, #d4d4d4);\n font-size: 0.875em;\n line-height: 1.5;\n}\n\n/* ============================================\n Code Toolbar (copy button, language label)\n ============================================ */\n.mj-markdown-container .code-toolbar {\n position: absolute;\n top: 8px;\n right: 8px;\n display: flex;\n align-items: center;\n gap: 8px;\n z-index: 10;\n}\n\n.mj-markdown-container .code-language-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n user-select: none;\n}\n\n.mj-markdown-container .code-copy-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: rgba(255, 255, 255, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-disabled);\n cursor: pointer;\n transition: all 0.2s ease;\n opacity: 0;\n}\n\n.mj-markdown-container pre:hover .code-copy-btn {\n opacity: 1;\n}\n\n.mj-markdown-container .code-copy-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n color: var(--mj-text-inverse);\n}\n\n.mj-markdown-container .code-copy-btn.copied {\n background: color-mix(in srgb, var(--mj-status-success) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, transparent);\n color: var(--mj-status-success);\n}\n\n.mj-markdown-container .code-copy-btn.error {\n background: color-mix(in srgb, var(--mj-status-error) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, transparent);\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Tables\n ============================================ */\n.mj-markdown-container table {\n width: 100%;\n margin: 1em 0;\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n.mj-markdown-container th,\n.mj-markdown-container td {\n padding: 0.5em 1em;\n border: 1px solid var(--mj-border-default);\n text-align: left;\n}\n\n.mj-markdown-container th {\n background: var(--mj-bg-surface-sunken);\n font-weight: 600;\n}\n\n.mj-markdown-container tr:nth-child(even) {\n background: var(--mj-bg-surface-sunken);\n}\n\n/* ============================================\n Horizontal Rule\n ============================================ */\n.mj-markdown-container hr {\n margin: 2em 0;\n border: none;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* ============================================\n Images\n ============================================ */\n.mj-markdown-container img {\n max-width: 100%;\n height: auto;\n border-radius: 4px;\n}\n\n/* ============================================\n GitHub-style Alerts (marked-alert)\n ============================================ */\n.mj-markdown-container .markdown-alert {\n margin: 1em 0;\n padding: 1em;\n border-radius: 8px;\n border-left: 4px solid;\n}\n\n.mj-markdown-container .markdown-alert-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n margin-bottom: 0.5em;\n}\n\n.mj-markdown-container .markdown-alert > p:last-child {\n margin-bottom: 0;\n}\n\n/* Note */\n.mj-markdown-container .markdown-alert-note {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-note .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Tip */\n.mj-markdown-container .markdown-alert-tip {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.mj-markdown-container .markdown-alert-tip .markdown-alert-title {\n color: var(--mj-status-success);\n}\n\n/* Important */\n.mj-markdown-container .markdown-alert-important {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-important .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Warning */\n.mj-markdown-container .markdown-alert-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.mj-markdown-container .markdown-alert-warning .markdown-alert-title {\n color: var(--mj-status-warning);\n}\n\n/* Caution */\n.mj-markdown-container .markdown-alert-caution {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-error);\n}\n\n.mj-markdown-container .markdown-alert-caution .markdown-alert-title {\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Collapsible Headings\n ============================================ */\n.mj-markdown-container .collapsible-section {\n margin: 0.75em 0;\n border-left: 2px solid transparent;\n transition: border-color 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section:hover {\n border-left-color: var(--mj-border-default);\n}\n\n/* Heading wrapper - contains toggle and heading */\n.mj-markdown-container .collapsible-heading-wrapper {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 0;\n border-radius: 4px;\n transition: background-color 0.15s ease;\n user-select: none;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 3%, transparent);\n}\n\n/* The heading itself */\n.mj-markdown-container .collapsible-heading {\n margin: 0 !important;\n flex: 1;\n}\n\n/* Toggle button/icon */\n.mj-markdown-container .collapsible-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n transition: transform 0.2s ease, color 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-toggle svg {\n width: 14px;\n height: 14px;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-toggle {\n color: var(--mj-text-secondary);\n}\n\n/* Expanded state - arrow points down */\n.mj-markdown-container .collapsible-section:not(.collapsed) .collapsible-toggle {\n transform: rotate(90deg);\n}\n\n/* Collapsed state - arrow points right (default SVG orientation) */\n.mj-markdown-container .collapsible-section.collapsed .collapsible-toggle {\n transform: rotate(0deg);\n}\n\n/* Content area */\n.mj-markdown-container .collapsible-content {\n padding-left: 26px;\n overflow: hidden;\n max-height: 5000px; /* Large enough for most content */\n opacity: 1;\n transition: max-height 0.3s ease-out, opacity 0.2s ease-out, padding 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: 0;\n opacity: 0;\n padding-top: 0;\n padding-bottom: 0;\n visibility: hidden;\n}\n\n/* Nested sections - indent further */\n.mj-markdown-container .collapsible-section .collapsible-section {\n margin-left: 0;\n}\n\n/* Focus styles for accessibility */\n.mj-markdown-container .collapsible-heading-wrapper:focus-within {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-markdown-container .collapsible-toggle:focus {\n outline: none;\n}\n\n/* Action buttons container - expand/collapse all */\n.mj-markdown-container .collapsible-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n margin-left: auto;\n padding-left: 12px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-actions {\n opacity: 1;\n}\n\n/* Individual action buttons */\n.mj-markdown-container .collapsible-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;\n}\n\n.mj-markdown-container .collapsible-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container .collapsible-action-btn:active {\n transform: scale(0.92);\n}\n\n.mj-markdown-container .collapsible-action-btn svg {\n width: 14px;\n height: 14px;\n}\n\n/* Expand all button - double down chevron */\n.mj-markdown-container .collapsible-action-btn.expand-all:hover {\n color: var(--mj-status-success);\n background-color: color-mix(in srgb, var(--mj-status-success) 10%, transparent);\n}\n\n/* Collapse all button - double up chevron */\n.mj-markdown-container .collapsible-action-btn.collapse-all:hover {\n color: var(--mj-status-error);\n background-color: color-mix(in srgb, var(--mj-status-error) 10%, transparent);\n}\n\n/* ============================================\n Mermaid Diagrams\n ============================================ */\n.mj-markdown-container .mermaid-diagram {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow-x: auto;\n}\n\n.mj-markdown-container .mermaid-diagram svg {\n max-width: 100%;\n height: auto;\n}\n\n.mj-markdown-container .mermaid-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n}\n\n.mj-markdown-container .mermaid-error::before {\n content: 'Diagram rendering failed';\n display: block;\n color: var(--mj-status-error);\n font-size: 12px;\n margin-bottom: 8px;\n}\n\n/* A specific reason, when we have one \u2014 e.g. the lazily-fetched diagram engine\n never arrived, which is a different problem than bad diagram source. */\n.mj-markdown-container .mermaid-error[data-mermaid-error]::before {\n content: attr(data-mermaid-error);\n}\n\n/* ============================================\n Heading Anchors\n ============================================ */\n.mj-markdown-container h1[id],\n.mj-markdown-container h2[id],\n.mj-markdown-container h3[id],\n.mj-markdown-container h4[id],\n.mj-markdown-container h5[id],\n.mj-markdown-container h6[id] {\n scroll-margin-top: 1em;\n}\n\n.mj-markdown-container h1[id]:hover,\n.mj-markdown-container h2[id]:hover,\n.mj-markdown-container h3[id]:hover,\n.mj-markdown-container h4[id]:hover,\n.mj-markdown-container h5[id]:hover,\n.mj-markdown-container h6[id]:hover {\n cursor: pointer;\n}\n\n/* Anchor link indicator on hover */\n.mj-markdown-container h1[id]::before,\n.mj-markdown-container h2[id]::before,\n.mj-markdown-container h3[id]::before,\n.mj-markdown-container h4[id]::before,\n.mj-markdown-container h5[id]::before,\n.mj-markdown-container h6[id]::before {\n content: '#';\n position: absolute;\n left: -1.5em;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.mj-markdown-container h1[id]:hover::before,\n.mj-markdown-container h2[id]:hover::before,\n.mj-markdown-container h3[id]:hover::before,\n.mj-markdown-container h4[id]:hover::before,\n.mj-markdown-container h5[id]:hover::before,\n.mj-markdown-container h6[id]:hover::before {\n opacity: 1;\n}\n\n/* ============================================\n Line Numbers (optional)\n ============================================ */\n.mj-markdown-container pre.line-numbers {\n padding-left: 3.5em;\n counter-reset: line;\n}\n\n.mj-markdown-container pre.line-numbers code {\n display: block;\n}\n\n.mj-markdown-container pre.line-numbers code .line::before {\n counter-increment: line;\n content: counter(line);\n display: inline-block;\n width: 2em;\n margin-left: -3em;\n margin-right: 1em;\n text-align: right;\n color: var(--mj-text-muted);\n user-select: none;\n}\n\n/* ============================================\n Error State\n ============================================ */\n.mj-markdown-container .markdown-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n padding: 1em;\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n white-space: pre-wrap;\n font-family: monospace;\n}\n\n/* Dark Mode Support \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n SVG Rendered Content\n ============================================ */\n.mj-markdown-container .svg-rendered {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow: auto;\n}\n\n.mj-markdown-container .svg-rendered svg {\n max-width: 100%;\n height: auto;\n}\n\n/* Dark mode for SVG rendered content \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n Print Styles\n ============================================ */\n@media print {\n .mj-markdown-container .code-toolbar {\n display: none;\n }\n\n .mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: none;\n opacity: 1;\n visibility: visible;\n }\n\n /* design-token-exempt: print always uses light background + dark text for\n paper readability, regardless of the screen theme the user is rendering from */\n .mj-markdown-container pre {\n background: #f3f4f6 !important;\n color: #1f2937 !important;\n }\n\n .mj-markdown-container pre code {\n color: #1f2937 !important;\n }\n}\n"], encapsulation: 2, changeDetection: 0 }); }
|
|
729
|
+
} }, styles: ["/**\n * MJ Markdown Component Styles\n *\n * These styles apply to rendered markdown content.\n * Using ViewEncapsulation.None so styles penetrate into dynamically generated content.\n */\n\n/* ============================================\n Container\n ============================================ */\n.mj-markdown-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 15px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n word-wrap: break-word;\n}\n\n/* ============================================\n Typography\n ============================================ */\n.mj-markdown-container h1,\n.mj-markdown-container h2,\n.mj-markdown-container h3,\n.mj-markdown-container h4,\n.mj-markdown-container h5,\n.mj-markdown-container h6 {\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.25;\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container h1 { font-size: 1.75em; }\n.mj-markdown-container h2 { font-size: 1.5em; }\n.mj-markdown-container h3 { font-size: 1.25em; }\n.mj-markdown-container h4 { font-size: 1.1em; }\n.mj-markdown-container h5 { font-size: 1em; }\n.mj-markdown-container h6 { font-size: 0.9em; color: var(--mj-text-secondary); }\n\n.mj-markdown-container > h1:first-child,\n.mj-markdown-container > h2:first-child,\n.mj-markdown-container > h3:first-child {\n margin-top: 0;\n}\n\n.mj-markdown-container p {\n margin: 0 0 1em 0;\n}\n\n.mj-markdown-container > p:last-child {\n margin-bottom: 0;\n}\n\n.mj-markdown-container a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n}\n\n.mj-markdown-container a:hover {\n text-decoration: underline;\n}\n\n.mj-markdown-container strong {\n font-weight: 600;\n}\n\n.mj-markdown-container em {\n font-style: italic;\n}\n\n/* ============================================\n Lists\n ============================================ */\n.mj-markdown-container ul,\n.mj-markdown-container ol {\n margin: 0 0 1em 0;\n padding-left: 2em;\n}\n\n.mj-markdown-container li {\n margin-bottom: 0.25em;\n}\n\n.mj-markdown-container li > ul,\n.mj-markdown-container li > ol {\n margin-top: 0.25em;\n margin-bottom: 0;\n}\n\n/* Task lists */\n.mj-markdown-container ul.contains-task-list {\n list-style: none;\n padding-left: 0;\n}\n\n.mj-markdown-container li.task-list-item {\n display: flex;\n align-items: flex-start;\n gap: 0.5em;\n}\n\n.mj-markdown-container li.task-list-item input[type=\"checkbox\"] {\n margin-top: 0.35em;\n}\n\n/* ============================================\n Blockquotes\n ============================================ */\n.mj-markdown-container blockquote {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 4px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.mj-markdown-container blockquote > p:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================\n Code Blocks\n ============================================ */\n.mj-markdown-container code {\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;\n font-size: 0.9em;\n}\n\n/* Inline code */\n.mj-markdown-container :not(pre) > code {\n padding: 0.2em 0.4em;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n color: var(--mj-status-error);\n}\n\n/* Code blocks\n * design-token-exempt fallbacks: --mj-bg-code-block / --mj-text-code resolve to\n * Monaco-style dark editor colors when an app does not override them. Code\n * blocks intentionally use a fixed dark editor aesthetic (same exemption as\n * Monaco/CodeMirror), so the hex fallbacks here are deliberate. */\n.mj-markdown-container pre {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-code-block, #1e1e1e);\n border-radius: var(--mj-radius-md);\n overflow-x: auto;\n position: relative;\n}\n\n.mj-markdown-container pre code {\n background: transparent;\n padding: 0;\n color: var(--mj-text-code, #d4d4d4);\n font-size: 0.875em;\n line-height: 1.5;\n}\n\n/* ============================================\n Code Toolbar (copy button, language label)\n ============================================ */\n.mj-markdown-container .code-toolbar {\n position: absolute;\n top: 8px;\n right: 8px;\n display: flex;\n align-items: center;\n gap: 8px;\n z-index: 10;\n}\n\n.mj-markdown-container .code-language-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n user-select: none;\n}\n\n.mj-markdown-container .code-copy-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: rgba(255, 255, 255, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-disabled);\n cursor: pointer;\n transition: all 0.2s ease;\n opacity: 0;\n}\n\n.mj-markdown-container pre:hover .code-copy-btn {\n opacity: 1;\n}\n\n.mj-markdown-container .code-copy-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n color: var(--mj-text-inverse);\n}\n\n.mj-markdown-container .code-copy-btn.copied {\n background: color-mix(in srgb, var(--mj-status-success) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, transparent);\n color: var(--mj-status-success);\n}\n\n.mj-markdown-container .code-copy-btn.error {\n background: color-mix(in srgb, var(--mj-status-error) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, transparent);\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Tables\n ============================================ */\n.mj-markdown-container table {\n width: 100%;\n margin: 1em 0;\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n.mj-markdown-container th,\n.mj-markdown-container td {\n padding: 0.5em 1em;\n border: 1px solid var(--mj-border-default);\n text-align: left;\n}\n\n.mj-markdown-container th {\n background: var(--mj-bg-surface-sunken);\n font-weight: 600;\n}\n\n.mj-markdown-container tr:nth-child(even) {\n background: var(--mj-bg-surface-sunken);\n}\n\n/* ============================================\n Horizontal Rule\n ============================================ */\n.mj-markdown-container hr {\n margin: 2em 0;\n border: none;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* ============================================\n Images\n ============================================ */\n.mj-markdown-container img {\n max-width: 100%;\n height: auto;\n border-radius: 4px;\n}\n\n/* ============================================\n GitHub-style Alerts (marked-alert)\n ============================================ */\n.mj-markdown-container .markdown-alert {\n margin: 1em 0;\n padding: 1em;\n border-radius: 8px;\n border-left: 4px solid;\n}\n\n.mj-markdown-container .markdown-alert-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n margin-bottom: 0.5em;\n}\n\n.mj-markdown-container .markdown-alert > p:last-child {\n margin-bottom: 0;\n}\n\n/* Note */\n.mj-markdown-container .markdown-alert-note {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-note .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Tip */\n.mj-markdown-container .markdown-alert-tip {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.mj-markdown-container .markdown-alert-tip .markdown-alert-title {\n color: var(--mj-status-success);\n}\n\n/* Important */\n.mj-markdown-container .markdown-alert-important {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-important .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Warning */\n.mj-markdown-container .markdown-alert-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.mj-markdown-container .markdown-alert-warning .markdown-alert-title {\n color: var(--mj-status-warning);\n}\n\n/* Caution */\n.mj-markdown-container .markdown-alert-caution {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-error);\n}\n\n.mj-markdown-container .markdown-alert-caution .markdown-alert-title {\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Collapsible Headings\n ============================================ */\n.mj-markdown-container .collapsible-section {\n margin: 0.75em 0;\n border-left: 2px solid transparent;\n transition: border-color 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section:hover {\n border-left-color: var(--mj-border-default);\n}\n\n/* Heading wrapper - contains toggle and heading */\n.mj-markdown-container .collapsible-heading-wrapper {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 0;\n border-radius: 4px;\n transition: background-color 0.15s ease;\n user-select: none;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 3%, transparent);\n}\n\n/* The heading itself */\n.mj-markdown-container .collapsible-heading {\n margin: 0 !important;\n flex: 1;\n}\n\n/* Toggle button/icon */\n.mj-markdown-container .collapsible-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n transition: transform 0.2s ease, color 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-toggle svg {\n width: 14px;\n height: 14px;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-toggle {\n color: var(--mj-text-secondary);\n}\n\n/* Expanded state - arrow points down */\n.mj-markdown-container .collapsible-section:not(.collapsed) .collapsible-toggle {\n transform: rotate(90deg);\n}\n\n/* Collapsed state - arrow points right (default SVG orientation) */\n.mj-markdown-container .collapsible-section.collapsed .collapsible-toggle {\n transform: rotate(0deg);\n}\n\n/* Content area */\n.mj-markdown-container .collapsible-content {\n padding-left: 26px;\n overflow: hidden;\n max-height: 5000px; /* Large enough for most content */\n opacity: 1;\n transition: max-height 0.3s ease-out, opacity 0.2s ease-out, padding 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: 0;\n opacity: 0;\n padding-top: 0;\n padding-bottom: 0;\n visibility: hidden;\n}\n\n/* Nested sections - indent further */\n.mj-markdown-container .collapsible-section .collapsible-section {\n margin-left: 0;\n}\n\n/* Focus styles for accessibility */\n.mj-markdown-container .collapsible-heading-wrapper:focus-within {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-markdown-container .collapsible-toggle:focus {\n outline: none;\n}\n\n/* Action buttons container - expand/collapse all */\n.mj-markdown-container .collapsible-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n margin-left: auto;\n padding-left: 12px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-actions {\n opacity: 1;\n}\n\n/* Individual action buttons */\n.mj-markdown-container .collapsible-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;\n}\n\n.mj-markdown-container .collapsible-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container .collapsible-action-btn:active {\n transform: scale(0.92);\n}\n\n.mj-markdown-container .collapsible-action-btn svg {\n width: 14px;\n height: 14px;\n}\n\n/* Expand all button - double down chevron */\n.mj-markdown-container .collapsible-action-btn.expand-all:hover {\n color: var(--mj-status-success);\n background-color: color-mix(in srgb, var(--mj-status-success) 10%, transparent);\n}\n\n/* Collapse all button - double up chevron */\n.mj-markdown-container .collapsible-action-btn.collapse-all:hover {\n color: var(--mj-status-error);\n background-color: color-mix(in srgb, var(--mj-status-error) 10%, transparent);\n}\n\n/* ============================================\n Math (TeX typeset as MathML by MarkdownService.RenderMath, and MathML markup)\n ============================================ */\n.mj-markdown-container .mj-math-block {\n display: block;\n margin: 1em 0;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n/* A wide display formula scrolls instead of widening the message. */\n.mj-markdown-container .katex:has(> math[display=\"block\"]) {\n display: block;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n/* Bold styles Unicode has no math letters for (Greek, text runs); Chrome ignores mathvariant. */\n.mj-markdown-container math [mathvariant*=\"bold\"] {\n font-weight: bold;\n}\n\n/* Chrome ignores MathML's columnalign too, which centres every cell and un-aligns the \"=\" signs of\n a derivation. KaTeX writes exactly \"right left\" (repeated, for alignedat) for aligned, split and\n alignedat, and \"left left\" for cases; JATS tables often say just \"left\" or \"right\". Other column\n lists (arrays) stay centred. */\n.mj-markdown-container mtable:is([columnalign=\"right left\"], [columnalign^=\"right left right left\"]) > mtr > mtd:nth-child(odd) {\n text-align: right;\n}\n\n.mj-markdown-container mtable:is([columnalign=\"right left\"], [columnalign^=\"right left right left\"]) > mtr > mtd:nth-child(even),\n.mj-markdown-container mtable:is([columnalign=\"left left\"], [columnalign=\"left\"]) > mtr > mtd {\n text-align: left;\n}\n\n.mj-markdown-container mtable[columnalign=\"right\"] > mtr > mtd {\n text-align: right;\n}\n\n/* TeX that KaTeX could not parse stays visible as source; its title says why. */\n.mj-markdown-container code.mj-math-error {\n text-decoration: underline wavy var(--mj-status-error);\n}\n\n/* ============================================\n Mermaid Diagrams\n ============================================ */\n.mj-markdown-container .mermaid-diagram {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow-x: auto;\n}\n\n.mj-markdown-container .mermaid-diagram svg {\n max-width: 100%;\n height: auto;\n}\n\n.mj-markdown-container .mermaid-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n}\n\n.mj-markdown-container .mermaid-error::before {\n content: 'Diagram rendering failed';\n display: block;\n color: var(--mj-status-error);\n font-size: 12px;\n margin-bottom: 8px;\n}\n\n/* A specific reason, when we have one \u2014 e.g. the lazily-fetched diagram engine\n never arrived, which is a different problem than bad diagram source. */\n.mj-markdown-container .mermaid-error[data-mermaid-error]::before {\n content: attr(data-mermaid-error);\n}\n\n/* ============================================\n Heading Anchors\n ============================================ */\n.mj-markdown-container h1[id],\n.mj-markdown-container h2[id],\n.mj-markdown-container h3[id],\n.mj-markdown-container h4[id],\n.mj-markdown-container h5[id],\n.mj-markdown-container h6[id] {\n scroll-margin-top: 1em;\n}\n\n.mj-markdown-container h1[id]:hover,\n.mj-markdown-container h2[id]:hover,\n.mj-markdown-container h3[id]:hover,\n.mj-markdown-container h4[id]:hover,\n.mj-markdown-container h5[id]:hover,\n.mj-markdown-container h6[id]:hover {\n cursor: pointer;\n}\n\n/* Anchor link indicator on hover */\n.mj-markdown-container h1[id]::before,\n.mj-markdown-container h2[id]::before,\n.mj-markdown-container h3[id]::before,\n.mj-markdown-container h4[id]::before,\n.mj-markdown-container h5[id]::before,\n.mj-markdown-container h6[id]::before {\n content: '#';\n position: absolute;\n left: -1.5em;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.mj-markdown-container h1[id]:hover::before,\n.mj-markdown-container h2[id]:hover::before,\n.mj-markdown-container h3[id]:hover::before,\n.mj-markdown-container h4[id]:hover::before,\n.mj-markdown-container h5[id]:hover::before,\n.mj-markdown-container h6[id]:hover::before {\n opacity: 1;\n}\n\n/* ============================================\n Line Numbers (optional)\n ============================================ */\n.mj-markdown-container pre.line-numbers {\n padding-left: 3.5em;\n counter-reset: line;\n}\n\n.mj-markdown-container pre.line-numbers code {\n display: block;\n}\n\n.mj-markdown-container pre.line-numbers code .line::before {\n counter-increment: line;\n content: counter(line);\n display: inline-block;\n width: 2em;\n margin-left: -3em;\n margin-right: 1em;\n text-align: right;\n color: var(--mj-text-muted);\n user-select: none;\n}\n\n/* ============================================\n Error State\n ============================================ */\n.mj-markdown-container .markdown-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n padding: 1em;\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n white-space: pre-wrap;\n font-family: monospace;\n}\n\n/* Dark Mode Support \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n SVG Rendered Content\n ============================================ */\n.mj-markdown-container .svg-rendered {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow: auto;\n}\n\n.mj-markdown-container .svg-rendered svg {\n max-width: 100%;\n height: auto;\n}\n\n/* Dark mode for SVG rendered content \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n Print Styles\n ============================================ */\n@media print {\n .mj-markdown-container .code-toolbar {\n display: none;\n }\n\n .mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: none;\n opacity: 1;\n visibility: visible;\n }\n\n /* design-token-exempt: print always uses light background + dark text for\n paper readability, regardless of the screen theme the user is rendering from */\n .mj-markdown-container pre {\n background: #f3f4f6 !important;\n color: #1f2937 !important;\n }\n\n .mj-markdown-container pre code {\n color: #1f2937 !important;\n }\n}\n"], encapsulation: 2, changeDetection: 0 }); }
|
|
712
730
|
}
|
|
713
731
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MarkdownComponent, [{
|
|
714
732
|
type: Component,
|
|
715
|
-
args: [{ selector: 'mj-markdown', standalone: false, template: ` <div class="mj-markdown-container" [class]="containerClass" [innerHTML]="renderedContent"></div> `, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["/**\n * MJ Markdown Component Styles\n *\n * These styles apply to rendered markdown content.\n * Using ViewEncapsulation.None so styles penetrate into dynamically generated content.\n */\n\n/* ============================================\n Container\n ============================================ */\n.mj-markdown-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 15px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n word-wrap: break-word;\n}\n\n/* ============================================\n Typography\n ============================================ */\n.mj-markdown-container h1,\n.mj-markdown-container h2,\n.mj-markdown-container h3,\n.mj-markdown-container h4,\n.mj-markdown-container h5,\n.mj-markdown-container h6 {\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.25;\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container h1 { font-size: 1.75em; }\n.mj-markdown-container h2 { font-size: 1.5em; }\n.mj-markdown-container h3 { font-size: 1.25em; }\n.mj-markdown-container h4 { font-size: 1.1em; }\n.mj-markdown-container h5 { font-size: 1em; }\n.mj-markdown-container h6 { font-size: 0.9em; color: var(--mj-text-secondary); }\n\n.mj-markdown-container > h1:first-child,\n.mj-markdown-container > h2:first-child,\n.mj-markdown-container > h3:first-child {\n margin-top: 0;\n}\n\n.mj-markdown-container p {\n margin: 0 0 1em 0;\n}\n\n.mj-markdown-container > p:last-child {\n margin-bottom: 0;\n}\n\n.mj-markdown-container a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n}\n\n.mj-markdown-container a:hover {\n text-decoration: underline;\n}\n\n.mj-markdown-container strong {\n font-weight: 600;\n}\n\n.mj-markdown-container em {\n font-style: italic;\n}\n\n/* ============================================\n Lists\n ============================================ */\n.mj-markdown-container ul,\n.mj-markdown-container ol {\n margin: 0 0 1em 0;\n padding-left: 2em;\n}\n\n.mj-markdown-container li {\n margin-bottom: 0.25em;\n}\n\n.mj-markdown-container li > ul,\n.mj-markdown-container li > ol {\n margin-top: 0.25em;\n margin-bottom: 0;\n}\n\n/* Task lists */\n.mj-markdown-container ul.contains-task-list {\n list-style: none;\n padding-left: 0;\n}\n\n.mj-markdown-container li.task-list-item {\n display: flex;\n align-items: flex-start;\n gap: 0.5em;\n}\n\n.mj-markdown-container li.task-list-item input[type=\"checkbox\"] {\n margin-top: 0.35em;\n}\n\n/* ============================================\n Blockquotes\n ============================================ */\n.mj-markdown-container blockquote {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 4px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.mj-markdown-container blockquote > p:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================\n Code Blocks\n ============================================ */\n.mj-markdown-container code {\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;\n font-size: 0.9em;\n}\n\n/* Inline code */\n.mj-markdown-container :not(pre) > code {\n padding: 0.2em 0.4em;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n color: var(--mj-status-error);\n}\n\n/* Code blocks\n * design-token-exempt fallbacks: --mj-bg-code-block / --mj-text-code resolve to\n * Monaco-style dark editor colors when an app does not override them. Code\n * blocks intentionally use a fixed dark editor aesthetic (same exemption as\n * Monaco/CodeMirror), so the hex fallbacks here are deliberate. */\n.mj-markdown-container pre {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-code-block, #1e1e1e);\n border-radius: var(--mj-radius-md);\n overflow-x: auto;\n position: relative;\n}\n\n.mj-markdown-container pre code {\n background: transparent;\n padding: 0;\n color: var(--mj-text-code, #d4d4d4);\n font-size: 0.875em;\n line-height: 1.5;\n}\n\n/* ============================================\n Code Toolbar (copy button, language label)\n ============================================ */\n.mj-markdown-container .code-toolbar {\n position: absolute;\n top: 8px;\n right: 8px;\n display: flex;\n align-items: center;\n gap: 8px;\n z-index: 10;\n}\n\n.mj-markdown-container .code-language-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n user-select: none;\n}\n\n.mj-markdown-container .code-copy-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: rgba(255, 255, 255, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-disabled);\n cursor: pointer;\n transition: all 0.2s ease;\n opacity: 0;\n}\n\n.mj-markdown-container pre:hover .code-copy-btn {\n opacity: 1;\n}\n\n.mj-markdown-container .code-copy-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n color: var(--mj-text-inverse);\n}\n\n.mj-markdown-container .code-copy-btn.copied {\n background: color-mix(in srgb, var(--mj-status-success) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, transparent);\n color: var(--mj-status-success);\n}\n\n.mj-markdown-container .code-copy-btn.error {\n background: color-mix(in srgb, var(--mj-status-error) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, transparent);\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Tables\n ============================================ */\n.mj-markdown-container table {\n width: 100%;\n margin: 1em 0;\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n.mj-markdown-container th,\n.mj-markdown-container td {\n padding: 0.5em 1em;\n border: 1px solid var(--mj-border-default);\n text-align: left;\n}\n\n.mj-markdown-container th {\n background: var(--mj-bg-surface-sunken);\n font-weight: 600;\n}\n\n.mj-markdown-container tr:nth-child(even) {\n background: var(--mj-bg-surface-sunken);\n}\n\n/* ============================================\n Horizontal Rule\n ============================================ */\n.mj-markdown-container hr {\n margin: 2em 0;\n border: none;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* ============================================\n Images\n ============================================ */\n.mj-markdown-container img {\n max-width: 100%;\n height: auto;\n border-radius: 4px;\n}\n\n/* ============================================\n GitHub-style Alerts (marked-alert)\n ============================================ */\n.mj-markdown-container .markdown-alert {\n margin: 1em 0;\n padding: 1em;\n border-radius: 8px;\n border-left: 4px solid;\n}\n\n.mj-markdown-container .markdown-alert-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n margin-bottom: 0.5em;\n}\n\n.mj-markdown-container .markdown-alert > p:last-child {\n margin-bottom: 0;\n}\n\n/* Note */\n.mj-markdown-container .markdown-alert-note {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-note .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Tip */\n.mj-markdown-container .markdown-alert-tip {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.mj-markdown-container .markdown-alert-tip .markdown-alert-title {\n color: var(--mj-status-success);\n}\n\n/* Important */\n.mj-markdown-container .markdown-alert-important {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-important .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Warning */\n.mj-markdown-container .markdown-alert-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.mj-markdown-container .markdown-alert-warning .markdown-alert-title {\n color: var(--mj-status-warning);\n}\n\n/* Caution */\n.mj-markdown-container .markdown-alert-caution {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-error);\n}\n\n.mj-markdown-container .markdown-alert-caution .markdown-alert-title {\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Collapsible Headings\n ============================================ */\n.mj-markdown-container .collapsible-section {\n margin: 0.75em 0;\n border-left: 2px solid transparent;\n transition: border-color 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section:hover {\n border-left-color: var(--mj-border-default);\n}\n\n/* Heading wrapper - contains toggle and heading */\n.mj-markdown-container .collapsible-heading-wrapper {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 0;\n border-radius: 4px;\n transition: background-color 0.15s ease;\n user-select: none;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 3%, transparent);\n}\n\n/* The heading itself */\n.mj-markdown-container .collapsible-heading {\n margin: 0 !important;\n flex: 1;\n}\n\n/* Toggle button/icon */\n.mj-markdown-container .collapsible-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n transition: transform 0.2s ease, color 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-toggle svg {\n width: 14px;\n height: 14px;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-toggle {\n color: var(--mj-text-secondary);\n}\n\n/* Expanded state - arrow points down */\n.mj-markdown-container .collapsible-section:not(.collapsed) .collapsible-toggle {\n transform: rotate(90deg);\n}\n\n/* Collapsed state - arrow points right (default SVG orientation) */\n.mj-markdown-container .collapsible-section.collapsed .collapsible-toggle {\n transform: rotate(0deg);\n}\n\n/* Content area */\n.mj-markdown-container .collapsible-content {\n padding-left: 26px;\n overflow: hidden;\n max-height: 5000px; /* Large enough for most content */\n opacity: 1;\n transition: max-height 0.3s ease-out, opacity 0.2s ease-out, padding 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: 0;\n opacity: 0;\n padding-top: 0;\n padding-bottom: 0;\n visibility: hidden;\n}\n\n/* Nested sections - indent further */\n.mj-markdown-container .collapsible-section .collapsible-section {\n margin-left: 0;\n}\n\n/* Focus styles for accessibility */\n.mj-markdown-container .collapsible-heading-wrapper:focus-within {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-markdown-container .collapsible-toggle:focus {\n outline: none;\n}\n\n/* Action buttons container - expand/collapse all */\n.mj-markdown-container .collapsible-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n margin-left: auto;\n padding-left: 12px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-actions {\n opacity: 1;\n}\n\n/* Individual action buttons */\n.mj-markdown-container .collapsible-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;\n}\n\n.mj-markdown-container .collapsible-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container .collapsible-action-btn:active {\n transform: scale(0.92);\n}\n\n.mj-markdown-container .collapsible-action-btn svg {\n width: 14px;\n height: 14px;\n}\n\n/* Expand all button - double down chevron */\n.mj-markdown-container .collapsible-action-btn.expand-all:hover {\n color: var(--mj-status-success);\n background-color: color-mix(in srgb, var(--mj-status-success) 10%, transparent);\n}\n\n/* Collapse all button - double up chevron */\n.mj-markdown-container .collapsible-action-btn.collapse-all:hover {\n color: var(--mj-status-error);\n background-color: color-mix(in srgb, var(--mj-status-error) 10%, transparent);\n}\n\n/* ============================================\n Mermaid Diagrams\n ============================================ */\n.mj-markdown-container .mermaid-diagram {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow-x: auto;\n}\n\n.mj-markdown-container .mermaid-diagram svg {\n max-width: 100%;\n height: auto;\n}\n\n.mj-markdown-container .mermaid-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n}\n\n.mj-markdown-container .mermaid-error::before {\n content: 'Diagram rendering failed';\n display: block;\n color: var(--mj-status-error);\n font-size: 12px;\n margin-bottom: 8px;\n}\n\n/* A specific reason, when we have one \u2014 e.g. the lazily-fetched diagram engine\n never arrived, which is a different problem than bad diagram source. */\n.mj-markdown-container .mermaid-error[data-mermaid-error]::before {\n content: attr(data-mermaid-error);\n}\n\n/* ============================================\n Heading Anchors\n ============================================ */\n.mj-markdown-container h1[id],\n.mj-markdown-container h2[id],\n.mj-markdown-container h3[id],\n.mj-markdown-container h4[id],\n.mj-markdown-container h5[id],\n.mj-markdown-container h6[id] {\n scroll-margin-top: 1em;\n}\n\n.mj-markdown-container h1[id]:hover,\n.mj-markdown-container h2[id]:hover,\n.mj-markdown-container h3[id]:hover,\n.mj-markdown-container h4[id]:hover,\n.mj-markdown-container h5[id]:hover,\n.mj-markdown-container h6[id]:hover {\n cursor: pointer;\n}\n\n/* Anchor link indicator on hover */\n.mj-markdown-container h1[id]::before,\n.mj-markdown-container h2[id]::before,\n.mj-markdown-container h3[id]::before,\n.mj-markdown-container h4[id]::before,\n.mj-markdown-container h5[id]::before,\n.mj-markdown-container h6[id]::before {\n content: '#';\n position: absolute;\n left: -1.5em;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.mj-markdown-container h1[id]:hover::before,\n.mj-markdown-container h2[id]:hover::before,\n.mj-markdown-container h3[id]:hover::before,\n.mj-markdown-container h4[id]:hover::before,\n.mj-markdown-container h5[id]:hover::before,\n.mj-markdown-container h6[id]:hover::before {\n opacity: 1;\n}\n\n/* ============================================\n Line Numbers (optional)\n ============================================ */\n.mj-markdown-container pre.line-numbers {\n padding-left: 3.5em;\n counter-reset: line;\n}\n\n.mj-markdown-container pre.line-numbers code {\n display: block;\n}\n\n.mj-markdown-container pre.line-numbers code .line::before {\n counter-increment: line;\n content: counter(line);\n display: inline-block;\n width: 2em;\n margin-left: -3em;\n margin-right: 1em;\n text-align: right;\n color: var(--mj-text-muted);\n user-select: none;\n}\n\n/* ============================================\n Error State\n ============================================ */\n.mj-markdown-container .markdown-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n padding: 1em;\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n white-space: pre-wrap;\n font-family: monospace;\n}\n\n/* Dark Mode Support \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n SVG Rendered Content\n ============================================ */\n.mj-markdown-container .svg-rendered {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow: auto;\n}\n\n.mj-markdown-container .svg-rendered svg {\n max-width: 100%;\n height: auto;\n}\n\n/* Dark mode for SVG rendered content \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n Print Styles\n ============================================ */\n@media print {\n .mj-markdown-container .code-toolbar {\n display: none;\n }\n\n .mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: none;\n opacity: 1;\n visibility: visible;\n }\n\n /* design-token-exempt: print always uses light background + dark text for\n paper readability, regardless of the screen theme the user is rendering from */\n .mj-markdown-container pre {\n background: #f3f4f6 !important;\n color: #1f2937 !important;\n }\n\n .mj-markdown-container pre code {\n color: #1f2937 !important;\n }\n}\n"] }]
|
|
733
|
+
args: [{ selector: 'mj-markdown', standalone: false, template: ` <div class="mj-markdown-container" [class]="containerClass" [innerHTML]="renderedContent"></div> `, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["/**\n * MJ Markdown Component Styles\n *\n * These styles apply to rendered markdown content.\n * Using ViewEncapsulation.None so styles penetrate into dynamically generated content.\n */\n\n/* ============================================\n Container\n ============================================ */\n.mj-markdown-container {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n font-size: 15px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n word-wrap: break-word;\n}\n\n/* ============================================\n Typography\n ============================================ */\n.mj-markdown-container h1,\n.mj-markdown-container h2,\n.mj-markdown-container h3,\n.mj-markdown-container h4,\n.mj-markdown-container h5,\n.mj-markdown-container h6 {\n margin-top: 1.5em;\n margin-bottom: 0.5em;\n font-weight: 600;\n line-height: 1.25;\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container h1 { font-size: 1.75em; }\n.mj-markdown-container h2 { font-size: 1.5em; }\n.mj-markdown-container h3 { font-size: 1.25em; }\n.mj-markdown-container h4 { font-size: 1.1em; }\n.mj-markdown-container h5 { font-size: 1em; }\n.mj-markdown-container h6 { font-size: 0.9em; color: var(--mj-text-secondary); }\n\n.mj-markdown-container > h1:first-child,\n.mj-markdown-container > h2:first-child,\n.mj-markdown-container > h3:first-child {\n margin-top: 0;\n}\n\n.mj-markdown-container p {\n margin: 0 0 1em 0;\n}\n\n.mj-markdown-container > p:last-child {\n margin-bottom: 0;\n}\n\n.mj-markdown-container a {\n color: var(--mj-brand-primary);\n text-decoration: none;\n}\n\n.mj-markdown-container a:hover {\n text-decoration: underline;\n}\n\n.mj-markdown-container strong {\n font-weight: 600;\n}\n\n.mj-markdown-container em {\n font-style: italic;\n}\n\n/* ============================================\n Lists\n ============================================ */\n.mj-markdown-container ul,\n.mj-markdown-container ol {\n margin: 0 0 1em 0;\n padding-left: 2em;\n}\n\n.mj-markdown-container li {\n margin-bottom: 0.25em;\n}\n\n.mj-markdown-container li > ul,\n.mj-markdown-container li > ol {\n margin-top: 0.25em;\n margin-bottom: 0;\n}\n\n/* Task lists */\n.mj-markdown-container ul.contains-task-list {\n list-style: none;\n padding-left: 0;\n}\n\n.mj-markdown-container li.task-list-item {\n display: flex;\n align-items: flex-start;\n gap: 0.5em;\n}\n\n.mj-markdown-container li.task-list-item input[type=\"checkbox\"] {\n margin-top: 0.35em;\n}\n\n/* ============================================\n Blockquotes\n ============================================ */\n.mj-markdown-container blockquote {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 4px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.mj-markdown-container blockquote > p:last-child {\n margin-bottom: 0;\n}\n\n/* ============================================\n Code Blocks\n ============================================ */\n.mj-markdown-container code {\n font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Courier New', monospace;\n font-size: 0.9em;\n}\n\n/* Inline code */\n.mj-markdown-container :not(pre) > code {\n padding: 0.2em 0.4em;\n background: var(--mj-bg-surface-sunken);\n border-radius: 4px;\n color: var(--mj-status-error);\n}\n\n/* Code blocks\n * design-token-exempt fallbacks: --mj-bg-code-block / --mj-text-code resolve to\n * Monaco-style dark editor colors when an app does not override them. Code\n * blocks intentionally use a fixed dark editor aesthetic (same exemption as\n * Monaco/CodeMirror), so the hex fallbacks here are deliberate. */\n.mj-markdown-container pre {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-code-block, #1e1e1e);\n border-radius: var(--mj-radius-md);\n overflow-x: auto;\n position: relative;\n}\n\n.mj-markdown-container pre code {\n background: transparent;\n padding: 0;\n color: var(--mj-text-code, #d4d4d4);\n font-size: 0.875em;\n line-height: 1.5;\n}\n\n/* ============================================\n Code Toolbar (copy button, language label)\n ============================================ */\n.mj-markdown-container .code-toolbar {\n position: absolute;\n top: 8px;\n right: 8px;\n display: flex;\n align-items: center;\n gap: 8px;\n z-index: 10;\n}\n\n.mj-markdown-container .code-language-label {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n user-select: none;\n}\n\n.mj-markdown-container .code-copy-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n background: rgba(255, 255, 255, 0.1);\n border: 1px solid rgba(255, 255, 255, 0.15);\n border-radius: var(--mj-radius-md);\n color: var(--mj-text-disabled);\n cursor: pointer;\n transition: all 0.2s ease;\n opacity: 0;\n}\n\n.mj-markdown-container pre:hover .code-copy-btn {\n opacity: 1;\n}\n\n.mj-markdown-container .code-copy-btn:hover {\n background: rgba(255, 255, 255, 0.2);\n color: var(--mj-text-inverse);\n}\n\n.mj-markdown-container .code-copy-btn.copied {\n background: color-mix(in srgb, var(--mj-status-success) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-success) 30%, transparent);\n color: var(--mj-status-success);\n}\n\n.mj-markdown-container .code-copy-btn.error {\n background: color-mix(in srgb, var(--mj-status-error) 20%, transparent);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, transparent);\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Tables\n ============================================ */\n.mj-markdown-container table {\n width: 100%;\n margin: 1em 0;\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n.mj-markdown-container th,\n.mj-markdown-container td {\n padding: 0.5em 1em;\n border: 1px solid var(--mj-border-default);\n text-align: left;\n}\n\n.mj-markdown-container th {\n background: var(--mj-bg-surface-sunken);\n font-weight: 600;\n}\n\n.mj-markdown-container tr:nth-child(even) {\n background: var(--mj-bg-surface-sunken);\n}\n\n/* ============================================\n Horizontal Rule\n ============================================ */\n.mj-markdown-container hr {\n margin: 2em 0;\n border: none;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* ============================================\n Images\n ============================================ */\n.mj-markdown-container img {\n max-width: 100%;\n height: auto;\n border-radius: 4px;\n}\n\n/* ============================================\n GitHub-style Alerts (marked-alert)\n ============================================ */\n.mj-markdown-container .markdown-alert {\n margin: 1em 0;\n padding: 1em;\n border-radius: 8px;\n border-left: 4px solid;\n}\n\n.mj-markdown-container .markdown-alert-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n margin-bottom: 0.5em;\n}\n\n.mj-markdown-container .markdown-alert > p:last-child {\n margin-bottom: 0;\n}\n\n/* Note */\n.mj-markdown-container .markdown-alert-note {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-note .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Tip */\n.mj-markdown-container .markdown-alert-tip {\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-success);\n}\n\n.mj-markdown-container .markdown-alert-tip .markdown-alert-title {\n color: var(--mj-status-success);\n}\n\n/* Important */\n.mj-markdown-container .markdown-alert-important {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n}\n\n.mj-markdown-container .markdown-alert-important .markdown-alert-title {\n color: var(--mj-brand-primary);\n}\n\n/* Warning */\n.mj-markdown-container .markdown-alert-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-warning);\n}\n\n.mj-markdown-container .markdown-alert-warning .markdown-alert-title {\n color: var(--mj-status-warning);\n}\n\n/* Caution */\n.mj-markdown-container .markdown-alert-caution {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-error);\n}\n\n.mj-markdown-container .markdown-alert-caution .markdown-alert-title {\n color: var(--mj-status-error);\n}\n\n/* ============================================\n Collapsible Headings\n ============================================ */\n.mj-markdown-container .collapsible-section {\n margin: 0.75em 0;\n border-left: 2px solid transparent;\n transition: border-color 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section:hover {\n border-left-color: var(--mj-border-default);\n}\n\n/* Heading wrapper - contains toggle and heading */\n.mj-markdown-container .collapsible-heading-wrapper {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 0;\n border-radius: 4px;\n transition: background-color 0.15s ease;\n user-select: none;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 3%, transparent);\n}\n\n/* The heading itself */\n.mj-markdown-container .collapsible-heading {\n margin: 0 !important;\n flex: 1;\n}\n\n/* Toggle button/icon */\n.mj-markdown-container .collapsible-toggle {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n flex-shrink: 0;\n color: var(--mj-text-muted);\n transition: transform 0.2s ease, color 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-toggle svg {\n width: 14px;\n height: 14px;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-toggle {\n color: var(--mj-text-secondary);\n}\n\n/* Expanded state - arrow points down */\n.mj-markdown-container .collapsible-section:not(.collapsed) .collapsible-toggle {\n transform: rotate(90deg);\n}\n\n/* Collapsed state - arrow points right (default SVG orientation) */\n.mj-markdown-container .collapsible-section.collapsed .collapsible-toggle {\n transform: rotate(0deg);\n}\n\n/* Content area */\n.mj-markdown-container .collapsible-content {\n padding-left: 26px;\n overflow: hidden;\n max-height: 5000px; /* Large enough for most content */\n opacity: 1;\n transition: max-height 0.3s ease-out, opacity 0.2s ease-out, padding 0.2s ease;\n}\n\n.mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: 0;\n opacity: 0;\n padding-top: 0;\n padding-bottom: 0;\n visibility: hidden;\n}\n\n/* Nested sections - indent further */\n.mj-markdown-container .collapsible-section .collapsible-section {\n margin-left: 0;\n}\n\n/* Focus styles for accessibility */\n.mj-markdown-container .collapsible-heading-wrapper:focus-within {\n outline: 2px solid var(--mj-brand-primary);\n outline-offset: 2px;\n}\n\n.mj-markdown-container .collapsible-toggle:focus {\n outline: none;\n}\n\n/* Action buttons container - expand/collapse all */\n.mj-markdown-container .collapsible-actions {\n display: flex;\n align-items: center;\n gap: 2px;\n margin-left: auto;\n padding-left: 12px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.mj-markdown-container .collapsible-heading-wrapper:hover .collapsible-actions {\n opacity: 1;\n}\n\n/* Individual action buttons */\n.mj-markdown-container .collapsible-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: background-color 0.15s ease, color 0.15s ease, transform 0.1s ease;\n}\n\n.mj-markdown-container .collapsible-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 8%, transparent);\n color: var(--mj-text-primary);\n}\n\n.mj-markdown-container .collapsible-action-btn:active {\n transform: scale(0.92);\n}\n\n.mj-markdown-container .collapsible-action-btn svg {\n width: 14px;\n height: 14px;\n}\n\n/* Expand all button - double down chevron */\n.mj-markdown-container .collapsible-action-btn.expand-all:hover {\n color: var(--mj-status-success);\n background-color: color-mix(in srgb, var(--mj-status-success) 10%, transparent);\n}\n\n/* Collapse all button - double up chevron */\n.mj-markdown-container .collapsible-action-btn.collapse-all:hover {\n color: var(--mj-status-error);\n background-color: color-mix(in srgb, var(--mj-status-error) 10%, transparent);\n}\n\n/* ============================================\n Math (TeX typeset as MathML by MarkdownService.RenderMath, and MathML markup)\n ============================================ */\n.mj-markdown-container .mj-math-block {\n display: block;\n margin: 1em 0;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n/* A wide display formula scrolls instead of widening the message. */\n.mj-markdown-container .katex:has(> math[display=\"block\"]) {\n display: block;\n overflow-x: auto;\n overflow-y: hidden;\n}\n\n/* Bold styles Unicode has no math letters for (Greek, text runs); Chrome ignores mathvariant. */\n.mj-markdown-container math [mathvariant*=\"bold\"] {\n font-weight: bold;\n}\n\n/* Chrome ignores MathML's columnalign too, which centres every cell and un-aligns the \"=\" signs of\n a derivation. KaTeX writes exactly \"right left\" (repeated, for alignedat) for aligned, split and\n alignedat, and \"left left\" for cases; JATS tables often say just \"left\" or \"right\". Other column\n lists (arrays) stay centred. */\n.mj-markdown-container mtable:is([columnalign=\"right left\"], [columnalign^=\"right left right left\"]) > mtr > mtd:nth-child(odd) {\n text-align: right;\n}\n\n.mj-markdown-container mtable:is([columnalign=\"right left\"], [columnalign^=\"right left right left\"]) > mtr > mtd:nth-child(even),\n.mj-markdown-container mtable:is([columnalign=\"left left\"], [columnalign=\"left\"]) > mtr > mtd {\n text-align: left;\n}\n\n.mj-markdown-container mtable[columnalign=\"right\"] > mtr > mtd {\n text-align: right;\n}\n\n/* TeX that KaTeX could not parse stays visible as source; its title says why. */\n.mj-markdown-container code.mj-math-error {\n text-decoration: underline wavy var(--mj-status-error);\n}\n\n/* ============================================\n Mermaid Diagrams\n ============================================ */\n.mj-markdown-container .mermaid-diagram {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow-x: auto;\n}\n\n.mj-markdown-container .mermaid-diagram svg {\n max-width: 100%;\n height: auto;\n}\n\n.mj-markdown-container .mermaid-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n}\n\n.mj-markdown-container .mermaid-error::before {\n content: 'Diagram rendering failed';\n display: block;\n color: var(--mj-status-error);\n font-size: 12px;\n margin-bottom: 8px;\n}\n\n/* A specific reason, when we have one \u2014 e.g. the lazily-fetched diagram engine\n never arrived, which is a different problem than bad diagram source. */\n.mj-markdown-container .mermaid-error[data-mermaid-error]::before {\n content: attr(data-mermaid-error);\n}\n\n/* ============================================\n Heading Anchors\n ============================================ */\n.mj-markdown-container h1[id],\n.mj-markdown-container h2[id],\n.mj-markdown-container h3[id],\n.mj-markdown-container h4[id],\n.mj-markdown-container h5[id],\n.mj-markdown-container h6[id] {\n scroll-margin-top: 1em;\n}\n\n.mj-markdown-container h1[id]:hover,\n.mj-markdown-container h2[id]:hover,\n.mj-markdown-container h3[id]:hover,\n.mj-markdown-container h4[id]:hover,\n.mj-markdown-container h5[id]:hover,\n.mj-markdown-container h6[id]:hover {\n cursor: pointer;\n}\n\n/* Anchor link indicator on hover */\n.mj-markdown-container h1[id]::before,\n.mj-markdown-container h2[id]::before,\n.mj-markdown-container h3[id]::before,\n.mj-markdown-container h4[id]::before,\n.mj-markdown-container h5[id]::before,\n.mj-markdown-container h6[id]::before {\n content: '#';\n position: absolute;\n left: -1.5em;\n color: var(--mj-text-muted);\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n\n.mj-markdown-container h1[id]:hover::before,\n.mj-markdown-container h2[id]:hover::before,\n.mj-markdown-container h3[id]:hover::before,\n.mj-markdown-container h4[id]:hover::before,\n.mj-markdown-container h5[id]:hover::before,\n.mj-markdown-container h6[id]:hover::before {\n opacity: 1;\n}\n\n/* ============================================\n Line Numbers (optional)\n ============================================ */\n.mj-markdown-container pre.line-numbers {\n padding-left: 3.5em;\n counter-reset: line;\n}\n\n.mj-markdown-container pre.line-numbers code {\n display: block;\n}\n\n.mj-markdown-container pre.line-numbers code .line::before {\n counter-increment: line;\n content: counter(line);\n display: inline-block;\n width: 2em;\n margin-left: -3em;\n margin-right: 1em;\n text-align: right;\n color: var(--mj-text-muted);\n user-select: none;\n}\n\n/* ============================================\n Error State\n ============================================ */\n.mj-markdown-container .markdown-error {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n padding: 1em;\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n white-space: pre-wrap;\n font-family: monospace;\n}\n\n/* Dark Mode Support \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n SVG Rendered Content\n ============================================ */\n.mj-markdown-container .svg-rendered {\n margin: 1em 0;\n padding: 1em;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n text-align: center;\n overflow: auto;\n}\n\n.mj-markdown-container .svg-rendered svg {\n max-width: 100%;\n height: auto;\n}\n\n/* Dark mode for SVG rendered content \u2014 handled automatically by --mj-* semantic tokens */\n\n/* ============================================\n Print Styles\n ============================================ */\n@media print {\n .mj-markdown-container .code-toolbar {\n display: none;\n }\n\n .mj-markdown-container .collapsible-section.collapsed .collapsible-content {\n max-height: none;\n opacity: 1;\n visibility: visible;\n }\n\n /* design-token-exempt: print always uses light background + dark text for\n paper readability, regardless of the screen theme the user is rendering from */\n .mj-markdown-container pre {\n background: #f3f4f6 !important;\n color: #1f2937 !important;\n }\n\n .mj-markdown-container pre code {\n color: #1f2937 !important;\n }\n}\n"] }]
|
|
716
734
|
}], () => [{ type: i0.ElementRef }, { type: i1.DomSanitizer }, { type: i2.MarkdownService }, { type: i0.ChangeDetectorRef }], { Data: [{
|
|
717
735
|
type: Input
|
|
718
736
|
}], data: [{
|
|
@@ -725,6 +743,8 @@ export class MarkdownComponent {
|
|
|
725
743
|
type: Input
|
|
726
744
|
}], enableMermaid: [{
|
|
727
745
|
type: Input
|
|
746
|
+
}], EnableMath: [{
|
|
747
|
+
type: Input
|
|
728
748
|
}], EnableCodeCopy: [{
|
|
729
749
|
type: Input
|
|
730
750
|
}], enableCodeCopy: [{
|