@memberjunction/ng-markdown 0.0.1 → 2.126.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,553 @@
1
+ import { Component, Input, Output, EventEmitter, ElementRef, SecurityContext, ViewEncapsulation, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
2
+ import { DomSanitizer } from '@angular/platform-browser';
3
+ import { MarkdownService } from '../services/markdown.service';
4
+ import { DEFAULT_MARKDOWN_CONFIG } from '../types/markdown.types';
5
+ import * as i0 from "@angular/core";
6
+ import * as i1 from "@angular/platform-browser";
7
+ import * as i2 from "../services/markdown.service";
8
+ // Collapsible section toggle is handled inline in setupCollapsibleListeners
9
+ /**
10
+ * Angular component for rendering markdown content.
11
+ *
12
+ * Features:
13
+ * - Prism.js syntax highlighting for code blocks
14
+ * - Mermaid diagram rendering
15
+ * - Copy-to-clipboard for code blocks
16
+ * - Collapsible heading sections
17
+ * - GitHub-style alerts and heading IDs
18
+ *
19
+ * Usage:
20
+ * ```html
21
+ * <mj-markdown [data]="markdownContent"></mj-markdown>
22
+ *
23
+ * <mj-markdown
24
+ * [data]="content"
25
+ * [enableMermaid]="true"
26
+ * [enableCollapsibleHeadings]="true"
27
+ * (rendered)="onRendered($event)">
28
+ * </mj-markdown>
29
+ * ```
30
+ */
31
+ export class MarkdownComponent {
32
+ /**
33
+ * Public accessor for the component's element reference.
34
+ * Provided for backward compatibility with ngx-markdown API.
35
+ */
36
+ get element() {
37
+ return this.elementRef;
38
+ }
39
+ constructor(elementRef, sanitizer, markdownService, cdr) {
40
+ this.elementRef = elementRef;
41
+ this.sanitizer = sanitizer;
42
+ this.markdownService = markdownService;
43
+ this.cdr = cdr;
44
+ /**
45
+ * The markdown content to render
46
+ */
47
+ this.data = '';
48
+ /**
49
+ * Enable syntax highlighting
50
+ */
51
+ this.enableHighlight = DEFAULT_MARKDOWN_CONFIG.enableHighlight;
52
+ /**
53
+ * Enable Mermaid diagram rendering
54
+ */
55
+ this.enableMermaid = DEFAULT_MARKDOWN_CONFIG.enableMermaid;
56
+ /**
57
+ * Enable copy button on code blocks
58
+ */
59
+ this.enableCodeCopy = DEFAULT_MARKDOWN_CONFIG.enableCodeCopy;
60
+ /**
61
+ * Enable collapsible heading sections
62
+ */
63
+ this.enableCollapsibleHeadings = DEFAULT_MARKDOWN_CONFIG.enableCollapsibleHeadings;
64
+ /**
65
+ * Heading level at which to start collapsing
66
+ */
67
+ this.collapsibleHeadingLevel = DEFAULT_MARKDOWN_CONFIG.collapsibleHeadingLevel;
68
+ /**
69
+ * Whether collapsible sections should be expanded by default
70
+ */
71
+ this.collapsibleDefaultExpanded = DEFAULT_MARKDOWN_CONFIG.collapsibleDefaultExpanded;
72
+ /**
73
+ * Enable GitHub-style alerts
74
+ */
75
+ this.enableAlerts = DEFAULT_MARKDOWN_CONFIG.enableAlerts;
76
+ /**
77
+ * Enable smartypants for typography (curly quotes, em/en dashes, ellipses)
78
+ */
79
+ this.enableSmartypants = DEFAULT_MARKDOWN_CONFIG.enableSmartypants;
80
+ /**
81
+ * Enable SVG code block rendering
82
+ * When enabled, ```svg code blocks are rendered as actual SVG images
83
+ */
84
+ this.enableSvgRenderer = DEFAULT_MARKDOWN_CONFIG.enableSvgRenderer;
85
+ /**
86
+ * Enable raw HTML passthrough in markdown content.
87
+ * Scripts and event handlers are still stripped unless enableJavaScript is true.
88
+ */
89
+ this.enableHtml = DEFAULT_MARKDOWN_CONFIG.enableHtml;
90
+ /**
91
+ * Enable JavaScript in HTML content (<script> tags and on* handlers).
92
+ * WARNING: Major security risk - only enable for fully trusted content.
93
+ */
94
+ this.enableJavaScript = DEFAULT_MARKDOWN_CONFIG.enableJavaScript;
95
+ /**
96
+ * Enable heading IDs for anchor links
97
+ */
98
+ this.enableHeadingIds = DEFAULT_MARKDOWN_CONFIG.enableHeadingIds;
99
+ /**
100
+ * Prefix for heading IDs
101
+ */
102
+ this.headingIdPrefix = DEFAULT_MARKDOWN_CONFIG.headingIdPrefix;
103
+ /**
104
+ * Enable line numbers in code blocks
105
+ */
106
+ this.enableLineNumbers = DEFAULT_MARKDOWN_CONFIG.enableLineNumbers;
107
+ /**
108
+ * Custom CSS class for the container
109
+ */
110
+ this.containerClass = '';
111
+ /**
112
+ * Mermaid theme
113
+ */
114
+ this.mermaidTheme = DEFAULT_MARKDOWN_CONFIG.mermaidTheme;
115
+ /**
116
+ * Whether to sanitize HTML output
117
+ */
118
+ this.sanitize = DEFAULT_MARKDOWN_CONFIG.sanitize;
119
+ /**
120
+ * Emitted when rendering is complete
121
+ */
122
+ this.rendered = new EventEmitter();
123
+ /**
124
+ * Emitted when a heading anchor is clicked
125
+ */
126
+ this.headingClick = new EventEmitter();
127
+ /**
128
+ * Emitted when code is copied to clipboard
129
+ */
130
+ this.codeCopied = new EventEmitter();
131
+ /**
132
+ * The sanitized HTML content to display
133
+ */
134
+ this.renderedContent = '';
135
+ this.renderStartTime = 0;
136
+ this.hasMermaid = false;
137
+ this.hasCodeBlocks = false;
138
+ }
139
+ ngOnChanges(changes) {
140
+ // Check if any relevant input changed
141
+ const needsRerender = changes['data'] ||
142
+ changes['enableHighlight'] ||
143
+ changes['enableMermaid'] ||
144
+ changes['enableCodeCopy'] ||
145
+ changes['enableCollapsibleHeadings'] ||
146
+ changes['collapsibleHeadingLevel'] ||
147
+ changes['collapsibleDefaultExpanded'] ||
148
+ changes['autoExpandLevels'] ||
149
+ changes['enableAlerts'] ||
150
+ changes['enableSmartypants'] ||
151
+ changes['enableSvgRenderer'] ||
152
+ changes['enableHtml'] ||
153
+ changes['enableJavaScript'] ||
154
+ changes['enableHeadingIds'] ||
155
+ changes['headingIdPrefix'] ||
156
+ changes['mermaidTheme'] ||
157
+ changes['sanitize'];
158
+ if (needsRerender) {
159
+ this.render();
160
+ }
161
+ }
162
+ ngAfterViewInit() {
163
+ // Initial render if data was provided
164
+ if (this.data) {
165
+ this.postRenderProcessing();
166
+ }
167
+ }
168
+ ngOnDestroy() {
169
+ // Cleanup any event listeners
170
+ this.cleanupEventListeners();
171
+ }
172
+ /**
173
+ * Render the markdown content
174
+ */
175
+ render() {
176
+ if (!this.data) {
177
+ this.renderedContent = '';
178
+ this.cdr.markForCheck();
179
+ return;
180
+ }
181
+ this.renderStartTime = performance.now();
182
+ // Build config from inputs
183
+ const config = {
184
+ enableHighlight: this.enableHighlight,
185
+ enableMermaid: this.enableMermaid,
186
+ enableCodeCopy: this.enableCodeCopy,
187
+ enableCollapsibleHeadings: this.enableCollapsibleHeadings,
188
+ collapsibleHeadingLevel: this.collapsibleHeadingLevel,
189
+ collapsibleDefaultExpanded: this.collapsibleDefaultExpanded,
190
+ autoExpandLevels: this.autoExpandLevels,
191
+ enableAlerts: this.enableAlerts,
192
+ enableSmartypants: this.enableSmartypants,
193
+ enableSvgRenderer: this.enableSvgRenderer,
194
+ enableHtml: this.enableHtml,
195
+ enableJavaScript: this.enableJavaScript,
196
+ enableHeadingIds: this.enableHeadingIds,
197
+ headingIdPrefix: this.headingIdPrefix,
198
+ mermaidTheme: this.mermaidTheme,
199
+ sanitize: this.sanitize
200
+ };
201
+ // Configure service and parse
202
+ this.markdownService.configureMarked(config);
203
+ let html = this.markdownService.parse(this.data);
204
+ // Check for mermaid and code blocks
205
+ this.hasMermaid = html.includes('language-mermaid') || html.includes('class="mermaid"');
206
+ this.hasCodeBlocks = html.includes('<pre>') && html.includes('<code');
207
+ // Sanitize if enabled
208
+ // Note: We bypass Angular's sanitizer when SVG renderer or HTML passthrough is enabled
209
+ // because it strips SVG elements and most HTML layout tags.
210
+ const bypassAngularSanitizer = this.enableSvgRenderer || this.enableHtml;
211
+ if (this.sanitize && !bypassAngularSanitizer) {
212
+ const sanitized = this.sanitizer.sanitize(SecurityContext.HTML, html);
213
+ html = sanitized || '';
214
+ }
215
+ // Strip JavaScript unless explicitly enabled
216
+ // This removes <script> tags and on* event handlers while keeping layout HTML
217
+ if (bypassAngularSanitizer && !this.enableJavaScript) {
218
+ html = this.stripJavaScript(html);
219
+ }
220
+ // Trust the HTML for display
221
+ this.renderedContent = this.sanitizer.bypassSecurityTrustHtml(html);
222
+ this.cdr.markForCheck();
223
+ // Schedule post-render processing for next tick (after DOM update)
224
+ Promise.resolve().then(() => this.postRenderProcessing());
225
+ }
226
+ /**
227
+ * Process rendered content after DOM update
228
+ * Handles syntax highlighting, mermaid rendering, copy buttons, etc.
229
+ */
230
+ async postRenderProcessing() {
231
+ const container = this.elementRef.nativeElement.querySelector('.mj-markdown-container');
232
+ if (!container)
233
+ return;
234
+ // Add copy buttons to code blocks
235
+ if (this.enableCodeCopy && this.hasCodeBlocks) {
236
+ this.markdownService.addCodeCopyButtons(container);
237
+ }
238
+ // Initialize collapsible headings
239
+ if (this.enableCollapsibleHeadings) {
240
+ this.markdownService.initializeCollapsibleHeadings(container);
241
+ this.setupCollapsibleListeners(container);
242
+ }
243
+ // Render mermaid diagrams (async)
244
+ if (this.enableMermaid && this.hasMermaid) {
245
+ await this.markdownService.renderMermaid(container);
246
+ }
247
+ // Setup heading click listeners
248
+ if (this.enableHeadingIds) {
249
+ this.setupHeadingClickListeners(container);
250
+ }
251
+ // Setup code copy listeners for custom event emission
252
+ if (this.enableCodeCopy) {
253
+ this.setupCodeCopyListeners(container);
254
+ }
255
+ // Emit rendered event
256
+ const renderTime = performance.now() - this.renderStartTime;
257
+ const headingIds = this.markdownService.getHeadingList();
258
+ this.rendered.emit({
259
+ html: container.innerHTML,
260
+ renderTime,
261
+ hasMermaid: this.hasMermaid,
262
+ hasCodeBlocks: this.hasCodeBlocks,
263
+ headingIds
264
+ });
265
+ }
266
+ /**
267
+ * Setup collapsible sections by adding toggle buttons and click listeners
268
+ */
269
+ setupCollapsibleListeners(container) {
270
+ const sections = container.querySelectorAll('.collapsible-section');
271
+ sections.forEach((section) => {
272
+ const wrapper = section.querySelector(':scope > .collapsible-heading-wrapper');
273
+ if (!wrapper)
274
+ return;
275
+ // Check if toggle already exists
276
+ if (wrapper.querySelector('.collapsible-toggle'))
277
+ return;
278
+ const isExpanded = !section.classList.contains('collapsed');
279
+ const hasChildren = section.querySelector('.collapsible-section') !== null;
280
+ // Create toggle button (chevron)
281
+ const toggle = document.createElement('span');
282
+ toggle.className = 'collapsible-toggle';
283
+ toggle.setAttribute('role', 'button');
284
+ toggle.setAttribute('tabindex', '0');
285
+ toggle.setAttribute('aria-expanded', String(isExpanded));
286
+ toggle.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
287
+ <path d="M6 12l4-4-4-4" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
288
+ </svg>`;
289
+ // Insert toggle before heading
290
+ wrapper.insertBefore(toggle, wrapper.firstChild);
291
+ // Add action buttons container (only if has children)
292
+ if (hasChildren) {
293
+ const actions = this.createActionButtons(section);
294
+ wrapper.appendChild(actions);
295
+ }
296
+ // Make the whole wrapper clickable
297
+ wrapper.style.cursor = 'pointer';
298
+ // Add click listener to wrapper (but not on action buttons)
299
+ wrapper.addEventListener('click', (e) => {
300
+ const target = e.target;
301
+ // Don't toggle if clicking on action buttons
302
+ if (target.closest('.collapsible-actions'))
303
+ return;
304
+ e.preventDefault();
305
+ e.stopPropagation();
306
+ this.toggleSection(section, toggle);
307
+ });
308
+ // Add keyboard support
309
+ wrapper.addEventListener('keydown', (e) => {
310
+ const target = e.target;
311
+ if (target.closest('.collapsible-actions'))
312
+ return;
313
+ const keyEvent = e;
314
+ if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {
315
+ e.preventDefault();
316
+ this.toggleSection(section, toggle);
317
+ }
318
+ });
319
+ });
320
+ }
321
+ /**
322
+ * Create the expand/collapse all action buttons for sections with children
323
+ */
324
+ createActionButtons(section) {
325
+ const container = document.createElement('span');
326
+ container.className = 'collapsible-actions';
327
+ // Expand all button
328
+ const expandBtn = document.createElement('button');
329
+ expandBtn.className = 'collapsible-action-btn expand-all';
330
+ expandBtn.setAttribute('type', 'button');
331
+ expandBtn.setAttribute('title', 'Expand all');
332
+ expandBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
333
+ <path d="M4 6l4 4 4-4" stroke-linecap="round" stroke-linejoin="round"/>
334
+ <path d="M4 10l4 4 4-4" stroke-linecap="round" stroke-linejoin="round"/>
335
+ </svg>`;
336
+ expandBtn.addEventListener('click', (e) => {
337
+ e.preventDefault();
338
+ e.stopPropagation();
339
+ this.expandDescendants(section);
340
+ // Also expand the section itself if collapsed
341
+ if (section.classList.contains('collapsed')) {
342
+ section.classList.remove('collapsed');
343
+ const toggle = section.querySelector(':scope > .collapsible-heading-wrapper .collapsible-toggle');
344
+ if (toggle)
345
+ toggle.setAttribute('aria-expanded', 'true');
346
+ }
347
+ });
348
+ // Collapse all button
349
+ const collapseBtn = document.createElement('button');
350
+ collapseBtn.className = 'collapsible-action-btn collapse-all';
351
+ collapseBtn.setAttribute('type', 'button');
352
+ collapseBtn.setAttribute('title', 'Collapse all');
353
+ collapseBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
354
+ <path d="M4 10l4-4 4 4" stroke-linecap="round" stroke-linejoin="round"/>
355
+ <path d="M4 14l4-4 4 4" stroke-linecap="round" stroke-linejoin="round"/>
356
+ </svg>`;
357
+ collapseBtn.addEventListener('click', (e) => {
358
+ e.preventDefault();
359
+ e.stopPropagation();
360
+ this.collapseDescendants(section);
361
+ });
362
+ container.appendChild(expandBtn);
363
+ container.appendChild(collapseBtn);
364
+ return container;
365
+ }
366
+ /**
367
+ * Toggle a collapsible section
368
+ * @param section The section element to toggle
369
+ * @param toggle The toggle button element
370
+ */
371
+ toggleSection(section, toggle) {
372
+ const isCollapsed = section.classList.contains('collapsed');
373
+ // Toggle the section
374
+ section.classList.toggle('collapsed');
375
+ toggle.setAttribute('aria-expanded', String(isCollapsed));
376
+ // Children retain their state - CSS handles visibility via parent collapse
377
+ }
378
+ /**
379
+ * Collapse all descendant sections (used by action button)
380
+ */
381
+ collapseDescendants(section) {
382
+ const descendants = section.querySelectorAll('.collapsible-section');
383
+ descendants.forEach((desc) => {
384
+ desc.classList.add('collapsed');
385
+ const toggle = desc.querySelector(':scope > .collapsible-heading-wrapper .collapsible-toggle');
386
+ if (toggle) {
387
+ toggle.setAttribute('aria-expanded', 'false');
388
+ }
389
+ });
390
+ }
391
+ /**
392
+ * Expand all descendant sections (used by action button)
393
+ */
394
+ expandDescendants(section) {
395
+ const descendants = section.querySelectorAll('.collapsible-section');
396
+ descendants.forEach((desc) => {
397
+ desc.classList.remove('collapsed');
398
+ const toggle = desc.querySelector(':scope > .collapsible-heading-wrapper .collapsible-toggle');
399
+ if (toggle) {
400
+ toggle.setAttribute('aria-expanded', 'true');
401
+ }
402
+ });
403
+ }
404
+ /**
405
+ * Setup click listeners for heading anchors
406
+ */
407
+ setupHeadingClickListeners(container) {
408
+ const headings = container.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');
409
+ headings.forEach((heading) => {
410
+ heading.addEventListener('click', () => {
411
+ const id = heading.getAttribute('id') || '';
412
+ const text = heading.textContent || '';
413
+ const level = parseInt(heading.tagName.charAt(1), 10);
414
+ this.headingClick.emit({
415
+ id,
416
+ text,
417
+ level,
418
+ raw: text
419
+ });
420
+ });
421
+ });
422
+ }
423
+ /**
424
+ * Setup listeners to emit code copy events
425
+ */
426
+ setupCodeCopyListeners(container) {
427
+ const copyButtons = container.querySelectorAll('.code-copy-btn');
428
+ copyButtons.forEach((button) => {
429
+ button.addEventListener('click', () => {
430
+ const pre = button.closest('pre');
431
+ const code = pre?.querySelector('code');
432
+ if (code) {
433
+ this.codeCopied.emit(code.textContent || '');
434
+ }
435
+ });
436
+ });
437
+ }
438
+ /**
439
+ * Cleanup event listeners
440
+ */
441
+ cleanupEventListeners() {
442
+ const container = this.elementRef.nativeElement.querySelector('.mj-markdown-container');
443
+ if (!container)
444
+ return;
445
+ // Clone and replace to remove all listeners
446
+ const clone = container.cloneNode(true);
447
+ container.parentNode?.replaceChild(clone, container);
448
+ }
449
+ /**
450
+ * Force a re-render of the markdown content
451
+ */
452
+ refresh() {
453
+ this.render();
454
+ }
455
+ /**
456
+ * Get the current heading list (for TOC building)
457
+ */
458
+ getHeadings() {
459
+ return this.markdownService.getHeadingList();
460
+ }
461
+ /**
462
+ * Scroll to a heading by ID
463
+ */
464
+ scrollToHeading(headingId) {
465
+ const container = this.elementRef.nativeElement.querySelector('.mj-markdown-container');
466
+ if (!container)
467
+ return;
468
+ const heading = container.querySelector(`#${headingId}`);
469
+ if (heading) {
470
+ heading.scrollIntoView({ behavior: 'smooth', block: 'start' });
471
+ }
472
+ }
473
+ /**
474
+ * Strip JavaScript from HTML content while preserving layout HTML.
475
+ * Removes <script> tags, on* event handlers, and javascript: URLs.
476
+ */
477
+ stripJavaScript(html) {
478
+ // Remove <script> tags and their content
479
+ html = html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
480
+ // Remove on* event handlers (onclick, onload, onerror, etc.)
481
+ html = html.replace(/\s+on\w+\s*=\s*["'][^"']*["']/gi, '');
482
+ html = html.replace(/\s+on\w+\s*=\s*[^\s>]+/gi, '');
483
+ // Remove javascript: URLs from href and src attributes
484
+ html = html.replace(/\s+href\s*=\s*["']javascript:[^"']*["']/gi, '');
485
+ html = html.replace(/\s+src\s*=\s*["']javascript:[^"']*["']/gi, '');
486
+ // Remove data: URLs that could contain scripts (data:text/html, etc.)
487
+ html = html.replace(/\s+src\s*=\s*["']data:text\/html[^"']*["']/gi, '');
488
+ return html;
489
+ }
490
+ static { this.ɵfac = function MarkdownComponent_Factory(t) { return new (t || MarkdownComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.DomSanitizer), i0.ɵɵdirectiveInject(i2.MarkdownService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); }; }
491
+ static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MarkdownComponent, selectors: [["mj-markdown"]], inputs: { data: "data", enableHighlight: "enableHighlight", enableMermaid: "enableMermaid", enableCodeCopy: "enableCodeCopy", enableCollapsibleHeadings: "enableCollapsibleHeadings", collapsibleHeadingLevel: "collapsibleHeadingLevel", collapsibleDefaultExpanded: "collapsibleDefaultExpanded", autoExpandLevels: "autoExpandLevels", enableAlerts: "enableAlerts", enableSmartypants: "enableSmartypants", enableSvgRenderer: "enableSvgRenderer", enableHtml: "enableHtml", enableJavaScript: "enableJavaScript", enableHeadingIds: "enableHeadingIds", headingIdPrefix: "headingIdPrefix", enableLineNumbers: "enableLineNumbers", containerClass: "containerClass", mermaidTheme: "mermaidTheme", sanitize: "sanitize" }, outputs: { rendered: "rendered", headingClick: "headingClick", codeCopied: "codeCopied" }, features: [i0.ɵɵNgOnChangesFeature], decls: 1, vars: 3, consts: [[1, "mj-markdown-container", 3, "innerHTML"]], template: function MarkdownComponent_Template(rf, ctx) { if (rf & 1) {
492
+ i0.ɵɵelement(0, "div", 0);
493
+ } if (rf & 2) {
494
+ i0.ɵɵclassMap(ctx.containerClass);
495
+ i0.ɵɵproperty("innerHTML", ctx.renderedContent, i0.ɵɵsanitizeHtml);
496
+ } }, 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: #1f2937;\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: #111827;\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: #6b7280; }\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: #3b82f6;\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 #e5e7eb;\n background: #f9fafb;\n color: #4b5563;\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: #f3f4f6;\n border-radius: 4px;\n color: #be185d;\n}\n\n/* Code blocks */\n.mj-markdown-container pre {\n margin: 1em 0;\n padding: 1em;\n background: #1e1e1e;\n border-radius: 8px;\n overflow-x: auto;\n position: relative;\n}\n\n.mj-markdown-container pre code {\n background: transparent;\n padding: 0;\n color: #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: #9ca3af;\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: 6px;\n color: #9ca3af;\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: #fff;\n}\n\n.mj-markdown-container .code-copy-btn.copied {\n background: rgba(34, 197, 94, 0.2);\n border-color: rgba(34, 197, 94, 0.3);\n color: #22c55e;\n}\n\n.mj-markdown-container .code-copy-btn.error {\n background: rgba(239, 68, 68, 0.2);\n border-color: rgba(239, 68, 68, 0.3);\n color: #ef4444;\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 #e5e7eb;\n text-align: left;\n}\n\n.mj-markdown-container th {\n background: #f9fafb;\n font-weight: 600;\n}\n\n.mj-markdown-container tr:nth-child(even) {\n background: #f9fafb;\n}\n\n/* ============================================\n Horizontal Rule\n ============================================ */\n.mj-markdown-container hr {\n margin: 2em 0;\n border: none;\n border-top: 1px solid #e5e7eb;\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: #eff6ff;\n border-color: #3b82f6;\n}\n\n.mj-markdown-container .markdown-alert-note .markdown-alert-title {\n color: #1d4ed8;\n}\n\n/* Tip */\n.mj-markdown-container .markdown-alert-tip {\n background: #f0fdf4;\n border-color: #22c55e;\n}\n\n.mj-markdown-container .markdown-alert-tip .markdown-alert-title {\n color: #16a34a;\n}\n\n/* Important */\n.mj-markdown-container .markdown-alert-important {\n background: #faf5ff;\n border-color: #a855f7;\n}\n\n.mj-markdown-container .markdown-alert-important .markdown-alert-title {\n color: #9333ea;\n}\n\n/* Warning */\n.mj-markdown-container .markdown-alert-warning {\n background: #fffbeb;\n border-color: #f59e0b;\n}\n\n.mj-markdown-container .markdown-alert-warning .markdown-alert-title {\n color: #d97706;\n}\n\n/* Caution */\n.mj-markdown-container .markdown-alert-caution {\n background: #fef2f2;\n border-color: #ef4444;\n}\n\n.mj-markdown-container .markdown-alert-caution .markdown-alert-title {\n color: #dc2626;\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: #e5e7eb;\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: rgba(0, 0, 0, 0.03);\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: #9ca3af;\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: #6b7280;\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 #3b82f6;\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: #9ca3af;\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: rgba(0, 0, 0, 0.08);\n color: #374151;\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: #059669;\n background-color: rgba(5, 150, 105, 0.1);\n}\n\n/* Collapse all button - double up chevron */\n.mj-markdown-container .collapsible-action-btn.collapse-all:hover {\n color: #dc2626;\n background-color: rgba(220, 38, 38, 0.1);\n}\n\n/* ============================================\n Mermaid Diagrams\n ============================================ */\n.mj-markdown-container .mermaid-diagram {\n margin: 1em 0;\n padding: 1em;\n background: #fff;\n border: 1px solid #e5e7eb;\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: #fef2f2;\n border-color: #fecaca;\n}\n\n.mj-markdown-container .mermaid-error::before {\n content: 'Diagram rendering failed';\n display: block;\n color: #dc2626;\n font-size: 12px;\n margin-bottom: 8px;\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: #9ca3af;\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: #6b7280;\n user-select: none;\n}\n\n/* ============================================\n Error State\n ============================================ */\n.mj-markdown-container .markdown-error {\n background: #fef2f2;\n color: #dc2626;\n padding: 1em;\n border-radius: 8px;\n border: 1px solid #fecaca;\n white-space: pre-wrap;\n font-family: monospace;\n}\n\n/* ============================================\n Dark Mode Support\n ============================================ */\n@media (prefers-color-scheme: dark) {\n .mj-markdown-container.dark-mode {\n color: #e5e7eb;\n }\n\n .mj-markdown-container.dark-mode h1,\n .mj-markdown-container.dark-mode h2,\n .mj-markdown-container.dark-mode h3,\n .mj-markdown-container.dark-mode h4,\n .mj-markdown-container.dark-mode h5,\n .mj-markdown-container.dark-mode h6 {\n color: #f9fafb;\n }\n\n .mj-markdown-container.dark-mode blockquote {\n background: #1f2937;\n border-color: #374151;\n color: #9ca3af;\n }\n\n .mj-markdown-container.dark-mode :not(pre) > code {\n background: #374151;\n color: #f472b6;\n }\n\n .mj-markdown-container.dark-mode table th {\n background: #1f2937;\n }\n\n .mj-markdown-container.dark-mode table tr:nth-child(even) {\n background: #1f2937;\n }\n\n .mj-markdown-container.dark-mode th,\n .mj-markdown-container.dark-mode td {\n border-color: #374151;\n }\n\n .mj-markdown-container.dark-mode hr {\n border-color: #374151;\n }\n}\n\n/* ============================================\n SVG Rendered Content\n ============================================ */\n.mj-markdown-container .svg-rendered {\n margin: 1em 0;\n padding: 1em;\n background: #ffffff;\n border: 1px solid #e5e7eb;\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 */\n@media (prefers-color-scheme: dark) {\n .mj-markdown-container.dark-mode .svg-rendered {\n background: #1f2937;\n border-color: #374151;\n }\n}\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 .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 }); }
497
+ }
498
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MarkdownComponent, [{
499
+ type: Component,
500
+ args: [{ selector: 'mj-markdown', template: `
501
+ <div
502
+ class="mj-markdown-container"
503
+ [class]="containerClass"
504
+ [innerHTML]="renderedContent">
505
+ </div>
506
+ `, 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: #1f2937;\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: #111827;\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: #6b7280; }\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: #3b82f6;\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 #e5e7eb;\n background: #f9fafb;\n color: #4b5563;\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: #f3f4f6;\n border-radius: 4px;\n color: #be185d;\n}\n\n/* Code blocks */\n.mj-markdown-container pre {\n margin: 1em 0;\n padding: 1em;\n background: #1e1e1e;\n border-radius: 8px;\n overflow-x: auto;\n position: relative;\n}\n\n.mj-markdown-container pre code {\n background: transparent;\n padding: 0;\n color: #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: #9ca3af;\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: 6px;\n color: #9ca3af;\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: #fff;\n}\n\n.mj-markdown-container .code-copy-btn.copied {\n background: rgba(34, 197, 94, 0.2);\n border-color: rgba(34, 197, 94, 0.3);\n color: #22c55e;\n}\n\n.mj-markdown-container .code-copy-btn.error {\n background: rgba(239, 68, 68, 0.2);\n border-color: rgba(239, 68, 68, 0.3);\n color: #ef4444;\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 #e5e7eb;\n text-align: left;\n}\n\n.mj-markdown-container th {\n background: #f9fafb;\n font-weight: 600;\n}\n\n.mj-markdown-container tr:nth-child(even) {\n background: #f9fafb;\n}\n\n/* ============================================\n Horizontal Rule\n ============================================ */\n.mj-markdown-container hr {\n margin: 2em 0;\n border: none;\n border-top: 1px solid #e5e7eb;\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: #eff6ff;\n border-color: #3b82f6;\n}\n\n.mj-markdown-container .markdown-alert-note .markdown-alert-title {\n color: #1d4ed8;\n}\n\n/* Tip */\n.mj-markdown-container .markdown-alert-tip {\n background: #f0fdf4;\n border-color: #22c55e;\n}\n\n.mj-markdown-container .markdown-alert-tip .markdown-alert-title {\n color: #16a34a;\n}\n\n/* Important */\n.mj-markdown-container .markdown-alert-important {\n background: #faf5ff;\n border-color: #a855f7;\n}\n\n.mj-markdown-container .markdown-alert-important .markdown-alert-title {\n color: #9333ea;\n}\n\n/* Warning */\n.mj-markdown-container .markdown-alert-warning {\n background: #fffbeb;\n border-color: #f59e0b;\n}\n\n.mj-markdown-container .markdown-alert-warning .markdown-alert-title {\n color: #d97706;\n}\n\n/* Caution */\n.mj-markdown-container .markdown-alert-caution {\n background: #fef2f2;\n border-color: #ef4444;\n}\n\n.mj-markdown-container .markdown-alert-caution .markdown-alert-title {\n color: #dc2626;\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: #e5e7eb;\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: rgba(0, 0, 0, 0.03);\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: #9ca3af;\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: #6b7280;\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 #3b82f6;\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: #9ca3af;\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: rgba(0, 0, 0, 0.08);\n color: #374151;\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: #059669;\n background-color: rgba(5, 150, 105, 0.1);\n}\n\n/* Collapse all button - double up chevron */\n.mj-markdown-container .collapsible-action-btn.collapse-all:hover {\n color: #dc2626;\n background-color: rgba(220, 38, 38, 0.1);\n}\n\n/* ============================================\n Mermaid Diagrams\n ============================================ */\n.mj-markdown-container .mermaid-diagram {\n margin: 1em 0;\n padding: 1em;\n background: #fff;\n border: 1px solid #e5e7eb;\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: #fef2f2;\n border-color: #fecaca;\n}\n\n.mj-markdown-container .mermaid-error::before {\n content: 'Diagram rendering failed';\n display: block;\n color: #dc2626;\n font-size: 12px;\n margin-bottom: 8px;\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: #9ca3af;\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: #6b7280;\n user-select: none;\n}\n\n/* ============================================\n Error State\n ============================================ */\n.mj-markdown-container .markdown-error {\n background: #fef2f2;\n color: #dc2626;\n padding: 1em;\n border-radius: 8px;\n border: 1px solid #fecaca;\n white-space: pre-wrap;\n font-family: monospace;\n}\n\n/* ============================================\n Dark Mode Support\n ============================================ */\n@media (prefers-color-scheme: dark) {\n .mj-markdown-container.dark-mode {\n color: #e5e7eb;\n }\n\n .mj-markdown-container.dark-mode h1,\n .mj-markdown-container.dark-mode h2,\n .mj-markdown-container.dark-mode h3,\n .mj-markdown-container.dark-mode h4,\n .mj-markdown-container.dark-mode h5,\n .mj-markdown-container.dark-mode h6 {\n color: #f9fafb;\n }\n\n .mj-markdown-container.dark-mode blockquote {\n background: #1f2937;\n border-color: #374151;\n color: #9ca3af;\n }\n\n .mj-markdown-container.dark-mode :not(pre) > code {\n background: #374151;\n color: #f472b6;\n }\n\n .mj-markdown-container.dark-mode table th {\n background: #1f2937;\n }\n\n .mj-markdown-container.dark-mode table tr:nth-child(even) {\n background: #1f2937;\n }\n\n .mj-markdown-container.dark-mode th,\n .mj-markdown-container.dark-mode td {\n border-color: #374151;\n }\n\n .mj-markdown-container.dark-mode hr {\n border-color: #374151;\n }\n}\n\n/* ============================================\n SVG Rendered Content\n ============================================ */\n.mj-markdown-container .svg-rendered {\n margin: 1em 0;\n padding: 1em;\n background: #ffffff;\n border: 1px solid #e5e7eb;\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 */\n@media (prefers-color-scheme: dark) {\n .mj-markdown-container.dark-mode .svg-rendered {\n background: #1f2937;\n border-color: #374151;\n }\n}\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 .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"] }]
507
+ }], () => [{ type: i0.ElementRef }, { type: i1.DomSanitizer }, { type: i2.MarkdownService }, { type: i0.ChangeDetectorRef }], { data: [{
508
+ type: Input
509
+ }], enableHighlight: [{
510
+ type: Input
511
+ }], enableMermaid: [{
512
+ type: Input
513
+ }], enableCodeCopy: [{
514
+ type: Input
515
+ }], enableCollapsibleHeadings: [{
516
+ type: Input
517
+ }], collapsibleHeadingLevel: [{
518
+ type: Input
519
+ }], collapsibleDefaultExpanded: [{
520
+ type: Input
521
+ }], autoExpandLevels: [{
522
+ type: Input
523
+ }], enableAlerts: [{
524
+ type: Input
525
+ }], enableSmartypants: [{
526
+ type: Input
527
+ }], enableSvgRenderer: [{
528
+ type: Input
529
+ }], enableHtml: [{
530
+ type: Input
531
+ }], enableJavaScript: [{
532
+ type: Input
533
+ }], enableHeadingIds: [{
534
+ type: Input
535
+ }], headingIdPrefix: [{
536
+ type: Input
537
+ }], enableLineNumbers: [{
538
+ type: Input
539
+ }], containerClass: [{
540
+ type: Input
541
+ }], mermaidTheme: [{
542
+ type: Input
543
+ }], sanitize: [{
544
+ type: Input
545
+ }], rendered: [{
546
+ type: Output
547
+ }], headingClick: [{
548
+ type: Output
549
+ }], codeCopied: [{
550
+ type: Output
551
+ }] }); })();
552
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MarkdownComponent, { className: "MarkdownComponent", filePath: "src/lib/components/markdown.component.ts", lineNumber: 61 }); })();
553
+ //# sourceMappingURL=markdown.component.js.map