@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.
- package/README.md +211 -43
- package/dist/lib/components/markdown.component.d.ts +210 -0
- package/dist/lib/components/markdown.component.js +553 -0
- package/dist/lib/components/markdown.component.js.map +1 -0
- package/dist/lib/extensions/code-copy.extension.d.ts +71 -0
- package/dist/lib/extensions/code-copy.extension.js +169 -0
- package/dist/lib/extensions/code-copy.extension.js.map +1 -0
- package/dist/lib/extensions/collapsible-headings.extension.d.ts +86 -0
- package/dist/lib/extensions/collapsible-headings.extension.js +240 -0
- package/dist/lib/extensions/collapsible-headings.extension.js.map +1 -0
- package/dist/lib/extensions/svg-renderer.extension.d.ts +38 -0
- package/dist/lib/extensions/svg-renderer.extension.js +126 -0
- package/dist/lib/extensions/svg-renderer.extension.js.map +1 -0
- package/dist/lib/markdown.module.d.ts +38 -0
- package/dist/lib/markdown.module.js +59 -0
- package/dist/lib/markdown.module.js.map +1 -0
- package/dist/lib/services/markdown.service.d.ts +101 -0
- package/dist/lib/services/markdown.service.js +310 -0
- package/dist/lib/services/markdown.service.js.map +1 -0
- package/dist/lib/types/markdown.types.d.ts +191 -0
- package/dist/lib/types/markdown.types.js +25 -0
- package/dist/lib/types/markdown.types.js.map +1 -0
- package/dist/public-api.d.ts +7 -0
- package/dist/public-api.js +14 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +44 -6
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { MarkdownConfig, HeadingInfo } from '../types/markdown.types';
|
|
2
|
+
import 'prismjs/components/prism-typescript';
|
|
3
|
+
import 'prismjs/components/prism-javascript';
|
|
4
|
+
import 'prismjs/components/prism-css';
|
|
5
|
+
import 'prismjs/components/prism-scss';
|
|
6
|
+
import 'prismjs/components/prism-json';
|
|
7
|
+
import 'prismjs/components/prism-bash';
|
|
8
|
+
import 'prismjs/components/prism-sql';
|
|
9
|
+
import 'prismjs/components/prism-python';
|
|
10
|
+
import 'prismjs/components/prism-csharp';
|
|
11
|
+
import 'prismjs/components/prism-java';
|
|
12
|
+
import 'prismjs/components/prism-markup';
|
|
13
|
+
import 'prismjs/components/prism-yaml';
|
|
14
|
+
import 'prismjs/components/prism-markdown';
|
|
15
|
+
import 'prismjs/components/prism-graphql';
|
|
16
|
+
import * as i0 from "@angular/core";
|
|
17
|
+
type ResolvedMarkdownConfig = Required<Omit<MarkdownConfig, 'autoExpandLevels'>> & {
|
|
18
|
+
autoExpandLevels?: number[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Service for parsing and rendering markdown content.
|
|
22
|
+
* Uses marked.js with various extensions for syntax highlighting,
|
|
23
|
+
* diagrams, alerts, and more.
|
|
24
|
+
*/
|
|
25
|
+
export declare class MarkdownService {
|
|
26
|
+
private marked;
|
|
27
|
+
private mermaidInitialized;
|
|
28
|
+
private currentConfig;
|
|
29
|
+
private headingList;
|
|
30
|
+
constructor();
|
|
31
|
+
/**
|
|
32
|
+
* Configure the marked instance with the provided options
|
|
33
|
+
*/
|
|
34
|
+
configureMarked(config: MarkdownConfig): void;
|
|
35
|
+
/**
|
|
36
|
+
* Initialize Mermaid with the current theme configuration
|
|
37
|
+
*/
|
|
38
|
+
private initializeMermaid;
|
|
39
|
+
/**
|
|
40
|
+
* Parse markdown to HTML
|
|
41
|
+
* @param markdown The markdown string to parse
|
|
42
|
+
* @param config Optional config overrides for this parse operation
|
|
43
|
+
* @returns The rendered HTML string
|
|
44
|
+
*/
|
|
45
|
+
parse(markdown: string, config?: Partial<MarkdownConfig>): string;
|
|
46
|
+
/**
|
|
47
|
+
* Parse markdown asynchronously (useful for large documents)
|
|
48
|
+
*/
|
|
49
|
+
parseAsync(markdown: string, config?: Partial<MarkdownConfig>): Promise<string>;
|
|
50
|
+
/**
|
|
51
|
+
* Render Mermaid diagrams in a container element
|
|
52
|
+
* Call this after the HTML has been inserted into the DOM
|
|
53
|
+
* @param container The DOM element containing mermaid code blocks
|
|
54
|
+
*/
|
|
55
|
+
renderMermaid(container: HTMLElement): Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* Highlight code blocks with Prism
|
|
58
|
+
* Call this after the HTML has been inserted into the DOM
|
|
59
|
+
* @param container The DOM element containing code blocks
|
|
60
|
+
*/
|
|
61
|
+
highlightCode(container: HTMLElement): void;
|
|
62
|
+
/**
|
|
63
|
+
* Add copy buttons to code blocks
|
|
64
|
+
* @param container The DOM element containing code blocks
|
|
65
|
+
*/
|
|
66
|
+
addCodeCopyButtons(container: HTMLElement): void;
|
|
67
|
+
/**
|
|
68
|
+
* Initialize collapsible heading functionality
|
|
69
|
+
* This method is a no-op - the component handles event binding
|
|
70
|
+
* @param container The DOM element containing collapsible sections
|
|
71
|
+
*/
|
|
72
|
+
initializeCollapsibleHeadings(_container: HTMLElement): void;
|
|
73
|
+
/**
|
|
74
|
+
* Get the list of headings from the last parsed document
|
|
75
|
+
* Useful for building table of contents
|
|
76
|
+
*/
|
|
77
|
+
getHeadingList(): HeadingInfo[];
|
|
78
|
+
/**
|
|
79
|
+
* Get the current configuration
|
|
80
|
+
*/
|
|
81
|
+
getConfig(): ResolvedMarkdownConfig;
|
|
82
|
+
/**
|
|
83
|
+
* Reset configuration to defaults
|
|
84
|
+
*/
|
|
85
|
+
resetConfig(): void;
|
|
86
|
+
/**
|
|
87
|
+
* Check if a language is supported by Prism
|
|
88
|
+
*/
|
|
89
|
+
isLanguageSupported(lang: string): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Get list of supported Prism languages
|
|
92
|
+
*/
|
|
93
|
+
getSupportedLanguages(): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Escape HTML entities for safe display
|
|
96
|
+
*/
|
|
97
|
+
private escapeHtml;
|
|
98
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MarkdownService, never>;
|
|
99
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<MarkdownService>;
|
|
100
|
+
}
|
|
101
|
+
export {};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { Injectable } from '@angular/core';
|
|
2
|
+
import { Marked } from 'marked';
|
|
3
|
+
import { markedHighlight } from 'marked-highlight';
|
|
4
|
+
import { gfmHeadingId, getHeadingList } from 'marked-gfm-heading-id';
|
|
5
|
+
import markedAlert from 'marked-alert';
|
|
6
|
+
import { markedSmartypants } from 'marked-smartypants';
|
|
7
|
+
import Prism from 'prismjs';
|
|
8
|
+
import mermaid from 'mermaid';
|
|
9
|
+
import { DEFAULT_MARKDOWN_CONFIG } from '../types/markdown.types';
|
|
10
|
+
import { createCollapsibleHeadingsExtension } from '../extensions/collapsible-headings.extension';
|
|
11
|
+
import { createSvgRendererExtension } from '../extensions/svg-renderer.extension';
|
|
12
|
+
// Import common Prism language components
|
|
13
|
+
// Additional languages can be imported by the consuming application
|
|
14
|
+
import 'prismjs/components/prism-typescript';
|
|
15
|
+
import 'prismjs/components/prism-javascript';
|
|
16
|
+
import 'prismjs/components/prism-css';
|
|
17
|
+
import 'prismjs/components/prism-scss';
|
|
18
|
+
import 'prismjs/components/prism-json';
|
|
19
|
+
import 'prismjs/components/prism-bash';
|
|
20
|
+
import 'prismjs/components/prism-sql';
|
|
21
|
+
import 'prismjs/components/prism-python';
|
|
22
|
+
import 'prismjs/components/prism-csharp';
|
|
23
|
+
import 'prismjs/components/prism-java';
|
|
24
|
+
import 'prismjs/components/prism-markup';
|
|
25
|
+
import 'prismjs/components/prism-yaml';
|
|
26
|
+
import 'prismjs/components/prism-markdown';
|
|
27
|
+
import 'prismjs/components/prism-graphql';
|
|
28
|
+
import * as i0 from "@angular/core";
|
|
29
|
+
/**
|
|
30
|
+
* Service for parsing and rendering markdown content.
|
|
31
|
+
* Uses marked.js with various extensions for syntax highlighting,
|
|
32
|
+
* diagrams, alerts, and more.
|
|
33
|
+
*/
|
|
34
|
+
export class MarkdownService {
|
|
35
|
+
constructor() {
|
|
36
|
+
this.mermaidInitialized = false;
|
|
37
|
+
this.currentConfig = { ...DEFAULT_MARKDOWN_CONFIG };
|
|
38
|
+
this.headingList = [];
|
|
39
|
+
this.marked = new Marked();
|
|
40
|
+
this.configureMarked(this.currentConfig);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Configure the marked instance with the provided options
|
|
44
|
+
*/
|
|
45
|
+
configureMarked(config) {
|
|
46
|
+
this.currentConfig = { ...DEFAULT_MARKDOWN_CONFIG, ...config };
|
|
47
|
+
// Create a fresh Marked instance
|
|
48
|
+
this.marked = new Marked();
|
|
49
|
+
// Configure base options
|
|
50
|
+
this.marked.setOptions({
|
|
51
|
+
gfm: true,
|
|
52
|
+
breaks: true
|
|
53
|
+
});
|
|
54
|
+
// Apply extensions based on config
|
|
55
|
+
const extensions = [];
|
|
56
|
+
// SVG code block renderer - MUST be before syntax highlighting
|
|
57
|
+
// so it can intercept svg blocks before Prism processes them
|
|
58
|
+
if (this.currentConfig.enableSvgRenderer) {
|
|
59
|
+
extensions.push(createSvgRendererExtension());
|
|
60
|
+
}
|
|
61
|
+
// Syntax highlighting with Prism
|
|
62
|
+
if (this.currentConfig.enableHighlight) {
|
|
63
|
+
extensions.push(markedHighlight({
|
|
64
|
+
langPrefix: 'language-',
|
|
65
|
+
highlight: (code, lang) => {
|
|
66
|
+
// Skip SVG blocks - they're handled by the SVG renderer
|
|
67
|
+
if (lang === 'svg' && this.currentConfig.enableSvgRenderer) {
|
|
68
|
+
return code;
|
|
69
|
+
}
|
|
70
|
+
if (lang && Prism.languages[lang]) {
|
|
71
|
+
try {
|
|
72
|
+
return Prism.highlight(code, Prism.languages[lang], lang);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.warn(`Prism highlighting failed for language: ${lang}`, e);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Return code as-is if language not found or highlighting fails
|
|
79
|
+
return code;
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
// GitHub-style heading IDs
|
|
84
|
+
if (this.currentConfig.enableHeadingIds) {
|
|
85
|
+
extensions.push(gfmHeadingId({
|
|
86
|
+
prefix: this.currentConfig.headingIdPrefix
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
// GitHub-style alerts
|
|
90
|
+
if (this.currentConfig.enableAlerts) {
|
|
91
|
+
extensions.push(markedAlert());
|
|
92
|
+
}
|
|
93
|
+
// Collapsible headings (custom extension)
|
|
94
|
+
if (this.currentConfig.enableCollapsibleHeadings) {
|
|
95
|
+
extensions.push(createCollapsibleHeadingsExtension({
|
|
96
|
+
startLevel: this.currentConfig.collapsibleHeadingLevel,
|
|
97
|
+
defaultExpanded: this.currentConfig.collapsibleDefaultExpanded,
|
|
98
|
+
autoExpandLevels: this.currentConfig.autoExpandLevels
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
// Smartypants for typography (curly quotes, em/en dashes, ellipses)
|
|
102
|
+
if (this.currentConfig.enableSmartypants) {
|
|
103
|
+
extensions.push(markedSmartypants());
|
|
104
|
+
}
|
|
105
|
+
// Apply all extensions
|
|
106
|
+
if (extensions.length > 0) {
|
|
107
|
+
this.marked.use(...extensions);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Initialize Mermaid with the current theme configuration
|
|
112
|
+
*/
|
|
113
|
+
initializeMermaid() {
|
|
114
|
+
if (this.mermaidInitialized)
|
|
115
|
+
return;
|
|
116
|
+
mermaid.initialize({
|
|
117
|
+
startOnLoad: false,
|
|
118
|
+
theme: this.currentConfig.mermaidTheme,
|
|
119
|
+
securityLevel: 'loose',
|
|
120
|
+
fontFamily: 'inherit'
|
|
121
|
+
});
|
|
122
|
+
this.mermaidInitialized = true;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Parse markdown to HTML
|
|
126
|
+
* @param markdown The markdown string to parse
|
|
127
|
+
* @param config Optional config overrides for this parse operation
|
|
128
|
+
* @returns The rendered HTML string
|
|
129
|
+
*/
|
|
130
|
+
parse(markdown, config) {
|
|
131
|
+
if (!markdown)
|
|
132
|
+
return '';
|
|
133
|
+
// Apply config overrides if provided
|
|
134
|
+
if (config) {
|
|
135
|
+
this.configureMarked({ ...this.currentConfig, ...config });
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const html = this.marked.parse(markdown);
|
|
139
|
+
// Capture heading list after parsing
|
|
140
|
+
if (this.currentConfig.enableHeadingIds) {
|
|
141
|
+
this.headingList = getHeadingList();
|
|
142
|
+
}
|
|
143
|
+
return html;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error('Markdown parsing error:', error);
|
|
147
|
+
return `<pre class="markdown-error">${this.escapeHtml(markdown)}</pre>`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Parse markdown asynchronously (useful for large documents)
|
|
152
|
+
*/
|
|
153
|
+
async parseAsync(markdown, config) {
|
|
154
|
+
return this.parse(markdown, config);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Render Mermaid diagrams in a container element
|
|
158
|
+
* Call this after the HTML has been inserted into the DOM
|
|
159
|
+
* @param container The DOM element containing mermaid code blocks
|
|
160
|
+
*/
|
|
161
|
+
async renderMermaid(container) {
|
|
162
|
+
if (!this.currentConfig.enableMermaid)
|
|
163
|
+
return false;
|
|
164
|
+
this.initializeMermaid();
|
|
165
|
+
// Find all mermaid code blocks
|
|
166
|
+
const mermaidBlocks = container.querySelectorAll('pre > code.language-mermaid, .mermaid');
|
|
167
|
+
if (mermaidBlocks.length === 0)
|
|
168
|
+
return false;
|
|
169
|
+
for (let i = 0; i < mermaidBlocks.length; i++) {
|
|
170
|
+
const block = mermaidBlocks[i];
|
|
171
|
+
const code = block.textContent || '';
|
|
172
|
+
if (!code.trim())
|
|
173
|
+
continue;
|
|
174
|
+
try {
|
|
175
|
+
// Create a unique ID for this diagram
|
|
176
|
+
const id = `mermaid-${Date.now()}-${i}`;
|
|
177
|
+
// Render the diagram
|
|
178
|
+
const { svg } = await mermaid.render(id, code);
|
|
179
|
+
// Replace the code block with the rendered SVG
|
|
180
|
+
const wrapper = document.createElement('div');
|
|
181
|
+
wrapper.className = 'mermaid-diagram';
|
|
182
|
+
wrapper.innerHTML = svg;
|
|
183
|
+
// Replace the pre element (parent of code) or the mermaid element itself
|
|
184
|
+
const elementToReplace = block.tagName === 'CODE' ? block.parentElement : block;
|
|
185
|
+
elementToReplace?.parentNode?.replaceChild(wrapper, elementToReplace);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.warn('Mermaid rendering failed:', error);
|
|
189
|
+
// Add error class to show it failed
|
|
190
|
+
const parent = block.tagName === 'CODE' ? block.parentElement : block;
|
|
191
|
+
parent?.classList.add('mermaid-error');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Highlight code blocks with Prism
|
|
198
|
+
* Call this after the HTML has been inserted into the DOM
|
|
199
|
+
* @param container The DOM element containing code blocks
|
|
200
|
+
*/
|
|
201
|
+
highlightCode(container) {
|
|
202
|
+
if (!this.currentConfig.enableHighlight)
|
|
203
|
+
return;
|
|
204
|
+
// Prism.highlightAllUnder handles finding and highlighting code blocks
|
|
205
|
+
Prism.highlightAllUnder(container);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Add copy buttons to code blocks
|
|
209
|
+
* @param container The DOM element containing code blocks
|
|
210
|
+
*/
|
|
211
|
+
addCodeCopyButtons(container) {
|
|
212
|
+
if (!this.currentConfig.enableCodeCopy)
|
|
213
|
+
return;
|
|
214
|
+
const codeBlocks = container.querySelectorAll('pre > code');
|
|
215
|
+
codeBlocks.forEach((codeBlock) => {
|
|
216
|
+
const pre = codeBlock.parentElement;
|
|
217
|
+
if (!pre || pre.querySelector('.code-copy-btn'))
|
|
218
|
+
return; // Already has button
|
|
219
|
+
// Create copy button
|
|
220
|
+
const button = document.createElement('button');
|
|
221
|
+
button.className = 'code-copy-btn';
|
|
222
|
+
button.innerHTML = '<i class="fas fa-copy"></i>';
|
|
223
|
+
button.title = 'Copy code';
|
|
224
|
+
button.type = 'button';
|
|
225
|
+
button.addEventListener('click', async () => {
|
|
226
|
+
const code = codeBlock.textContent || '';
|
|
227
|
+
try {
|
|
228
|
+
await navigator.clipboard.writeText(code);
|
|
229
|
+
button.innerHTML = '<i class="fas fa-check"></i>';
|
|
230
|
+
button.classList.add('copied');
|
|
231
|
+
setTimeout(() => {
|
|
232
|
+
button.innerHTML = '<i class="fas fa-copy"></i>';
|
|
233
|
+
button.classList.remove('copied');
|
|
234
|
+
}, 2000);
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
console.error('Failed to copy code:', err);
|
|
238
|
+
button.innerHTML = '<i class="fas fa-times"></i>';
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
button.innerHTML = '<i class="fas fa-copy"></i>';
|
|
241
|
+
}, 2000);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
// Add toolbar wrapper
|
|
245
|
+
const toolbar = document.createElement('div');
|
|
246
|
+
toolbar.className = 'code-toolbar';
|
|
247
|
+
toolbar.appendChild(button);
|
|
248
|
+
// Make pre position relative for absolute positioning of toolbar
|
|
249
|
+
pre.style.position = 'relative';
|
|
250
|
+
pre.appendChild(toolbar);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Initialize collapsible heading functionality
|
|
255
|
+
* This method is a no-op - the component handles event binding
|
|
256
|
+
* @param container The DOM element containing collapsible sections
|
|
257
|
+
*/
|
|
258
|
+
initializeCollapsibleHeadings(_container) {
|
|
259
|
+
// Event binding is handled by the component's setupCollapsibleListeners
|
|
260
|
+
// This method exists for API compatibility but does nothing
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get the list of headings from the last parsed document
|
|
264
|
+
* Useful for building table of contents
|
|
265
|
+
*/
|
|
266
|
+
getHeadingList() {
|
|
267
|
+
return this.headingList;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get the current configuration
|
|
271
|
+
*/
|
|
272
|
+
getConfig() {
|
|
273
|
+
return { ...this.currentConfig };
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Reset configuration to defaults
|
|
277
|
+
*/
|
|
278
|
+
resetConfig() {
|
|
279
|
+
this.configureMarked(DEFAULT_MARKDOWN_CONFIG);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Check if a language is supported by Prism
|
|
283
|
+
*/
|
|
284
|
+
isLanguageSupported(lang) {
|
|
285
|
+
return !!Prism.languages[lang];
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Get list of supported Prism languages
|
|
289
|
+
*/
|
|
290
|
+
getSupportedLanguages() {
|
|
291
|
+
return Object.keys(Prism.languages).filter(lang => typeof Prism.languages[lang] === 'object');
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Escape HTML entities for safe display
|
|
295
|
+
*/
|
|
296
|
+
escapeHtml(text) {
|
|
297
|
+
const div = document.createElement('div');
|
|
298
|
+
div.textContent = text;
|
|
299
|
+
return div.innerHTML;
|
|
300
|
+
}
|
|
301
|
+
static { this.ɵfac = function MarkdownService_Factory(t) { return new (t || MarkdownService)(); }; }
|
|
302
|
+
static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: MarkdownService, factory: MarkdownService.ɵfac, providedIn: 'root' }); }
|
|
303
|
+
}
|
|
304
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MarkdownService, [{
|
|
305
|
+
type: Injectable,
|
|
306
|
+
args: [{
|
|
307
|
+
providedIn: 'root'
|
|
308
|
+
}]
|
|
309
|
+
}], () => [], null); })();
|
|
310
|
+
//# sourceMappingURL=markdown.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.service.js","sourceRoot":"","sources":["../../../src/lib/services/markdown.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Injectable } from '@angular/core';\nimport { Marked } from 'marked';\nimport { markedHighlight } from 'marked-highlight';\nimport { gfmHeadingId, getHeadingList } from 'marked-gfm-heading-id';\nimport markedAlert from 'marked-alert';\nimport { markedSmartypants } from 'marked-smartypants';\nimport Prism from 'prismjs';\nimport mermaid from 'mermaid';\nimport {\n MarkdownConfig,\n DEFAULT_MARKDOWN_CONFIG,\n HeadingInfo,\n MarkdownRenderEvent\n} from '../types/markdown.types';\nimport { createCollapsibleHeadingsExtension } from '../extensions/collapsible-headings.extension';\nimport { createSvgRendererExtension } from '../extensions/svg-renderer.extension';\n\n// Import common Prism language components\n// Additional languages can be imported by the consuming application\nimport 'prismjs/components/prism-typescript';\nimport 'prismjs/components/prism-javascript';\nimport 'prismjs/components/prism-css';\nimport 'prismjs/components/prism-scss';\nimport 'prismjs/components/prism-json';\nimport 'prismjs/components/prism-bash';\nimport 'prismjs/components/prism-sql';\nimport 'prismjs/components/prism-python';\nimport 'prismjs/components/prism-csharp';\nimport 'prismjs/components/prism-java';\nimport 'prismjs/components/prism-markup';\nimport 'prismjs/components/prism-yaml';\nimport 'prismjs/components/prism-markdown';\nimport 'prismjs/components/prism-graphql';\n\n// Type for config with optional autoExpandLevels\ntype ResolvedMarkdownConfig = Required<Omit<MarkdownConfig, 'autoExpandLevels'>> & { autoExpandLevels?: number[] };\n\n/**\n * Service for parsing and rendering markdown content.\n * Uses marked.js with various extensions for syntax highlighting,\n * diagrams, alerts, and more.\n */\n@Injectable({\n providedIn: 'root'\n})\nexport class MarkdownService {\n private marked: Marked;\n private mermaidInitialized = false;\n private currentConfig: ResolvedMarkdownConfig = { ...DEFAULT_MARKDOWN_CONFIG };\n private headingList: HeadingInfo[] = [];\n\n constructor() {\n this.marked = new Marked();\n this.configureMarked(this.currentConfig);\n }\n\n /**\n * Configure the marked instance with the provided options\n */\n public configureMarked(config: MarkdownConfig): void {\n this.currentConfig = { ...DEFAULT_MARKDOWN_CONFIG, ...config };\n\n // Create a fresh Marked instance\n this.marked = new Marked();\n\n // Configure base options\n this.marked.setOptions({\n gfm: true,\n breaks: true\n });\n\n // Apply extensions based on config\n const extensions: any[] = [];\n\n // SVG code block renderer - MUST be before syntax highlighting\n // so it can intercept svg blocks before Prism processes them\n if (this.currentConfig.enableSvgRenderer) {\n extensions.push(createSvgRendererExtension());\n }\n\n // Syntax highlighting with Prism\n if (this.currentConfig.enableHighlight) {\n extensions.push(\n markedHighlight({\n langPrefix: 'language-',\n highlight: (code: string, lang: string) => {\n // Skip SVG blocks - they're handled by the SVG renderer\n if (lang === 'svg' && this.currentConfig.enableSvgRenderer) {\n return code;\n }\n if (lang && Prism.languages[lang]) {\n try {\n return Prism.highlight(code, Prism.languages[lang], lang);\n } catch (e) {\n console.warn(`Prism highlighting failed for language: ${lang}`, e);\n }\n }\n // Return code as-is if language not found or highlighting fails\n return code;\n }\n })\n );\n }\n\n // GitHub-style heading IDs\n if (this.currentConfig.enableHeadingIds) {\n extensions.push(\n gfmHeadingId({\n prefix: this.currentConfig.headingIdPrefix\n })\n );\n }\n\n // GitHub-style alerts\n if (this.currentConfig.enableAlerts) {\n extensions.push(markedAlert());\n }\n\n // Collapsible headings (custom extension)\n if (this.currentConfig.enableCollapsibleHeadings) {\n extensions.push(\n createCollapsibleHeadingsExtension({\n startLevel: this.currentConfig.collapsibleHeadingLevel,\n defaultExpanded: this.currentConfig.collapsibleDefaultExpanded,\n autoExpandLevels: this.currentConfig.autoExpandLevels\n })\n );\n }\n\n // Smartypants for typography (curly quotes, em/en dashes, ellipses)\n if (this.currentConfig.enableSmartypants) {\n extensions.push(markedSmartypants());\n }\n\n // Apply all extensions\n if (extensions.length > 0) {\n this.marked.use(...extensions);\n }\n }\n\n /**\n * Initialize Mermaid with the current theme configuration\n */\n private initializeMermaid(): void {\n if (this.mermaidInitialized) return;\n\n mermaid.initialize({\n startOnLoad: false,\n theme: this.currentConfig.mermaidTheme,\n securityLevel: 'loose',\n fontFamily: 'inherit'\n });\n\n this.mermaidInitialized = true;\n }\n\n /**\n * Parse markdown to HTML\n * @param markdown The markdown string to parse\n * @param config Optional config overrides for this parse operation\n * @returns The rendered HTML string\n */\n public parse(markdown: string, config?: Partial<MarkdownConfig>): string {\n if (!markdown) return '';\n\n // Apply config overrides if provided\n if (config) {\n this.configureMarked({ ...this.currentConfig, ...config });\n }\n\n try {\n const html = this.marked.parse(markdown) as string;\n\n // Capture heading list after parsing\n if (this.currentConfig.enableHeadingIds) {\n this.headingList = getHeadingList() as HeadingInfo[];\n }\n\n return html;\n } catch (error) {\n console.error('Markdown parsing error:', error);\n return `<pre class=\"markdown-error\">${this.escapeHtml(markdown)}</pre>`;\n }\n }\n\n /**\n * Parse markdown asynchronously (useful for large documents)\n */\n public async parseAsync(markdown: string, config?: Partial<MarkdownConfig>): Promise<string> {\n return this.parse(markdown, config);\n }\n\n /**\n * Render Mermaid diagrams in a container element\n * Call this after the HTML has been inserted into the DOM\n * @param container The DOM element containing mermaid code blocks\n */\n public async renderMermaid(container: HTMLElement): Promise<boolean> {\n if (!this.currentConfig.enableMermaid) return false;\n\n this.initializeMermaid();\n\n // Find all mermaid code blocks\n const mermaidBlocks = container.querySelectorAll('pre > code.language-mermaid, .mermaid');\n\n if (mermaidBlocks.length === 0) return false;\n\n for (let i = 0; i < mermaidBlocks.length; i++) {\n const block = mermaidBlocks[i];\n const code = block.textContent || '';\n\n if (!code.trim()) continue;\n\n try {\n // Create a unique ID for this diagram\n const id = `mermaid-${Date.now()}-${i}`;\n\n // Render the diagram\n const { svg } = await mermaid.render(id, code);\n\n // Replace the code block with the rendered SVG\n const wrapper = document.createElement('div');\n wrapper.className = 'mermaid-diagram';\n wrapper.innerHTML = svg;\n\n // Replace the pre element (parent of code) or the mermaid element itself\n const elementToReplace = block.tagName === 'CODE' ? block.parentElement : block;\n elementToReplace?.parentNode?.replaceChild(wrapper, elementToReplace);\n } catch (error) {\n console.warn('Mermaid rendering failed:', error);\n // Add error class to show it failed\n const parent = block.tagName === 'CODE' ? block.parentElement : block;\n parent?.classList.add('mermaid-error');\n }\n }\n\n return true;\n }\n\n /**\n * Highlight code blocks with Prism\n * Call this after the HTML has been inserted into the DOM\n * @param container The DOM element containing code blocks\n */\n public highlightCode(container: HTMLElement): void {\n if (!this.currentConfig.enableHighlight) return;\n\n // Prism.highlightAllUnder handles finding and highlighting code blocks\n Prism.highlightAllUnder(container);\n }\n\n /**\n * Add copy buttons to code blocks\n * @param container The DOM element containing code blocks\n */\n public addCodeCopyButtons(container: HTMLElement): void {\n if (!this.currentConfig.enableCodeCopy) return;\n\n const codeBlocks = container.querySelectorAll('pre > code');\n\n codeBlocks.forEach((codeBlock) => {\n const pre = codeBlock.parentElement;\n if (!pre || pre.querySelector('.code-copy-btn')) return; // Already has button\n\n // Create copy button\n const button = document.createElement('button');\n button.className = 'code-copy-btn';\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n button.title = 'Copy code';\n button.type = 'button';\n\n button.addEventListener('click', async () => {\n const code = codeBlock.textContent || '';\n\n try {\n await navigator.clipboard.writeText(code);\n button.innerHTML = '<i class=\"fas fa-check\"></i>';\n button.classList.add('copied');\n\n setTimeout(() => {\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n button.classList.remove('copied');\n }, 2000);\n } catch (err) {\n console.error('Failed to copy code:', err);\n button.innerHTML = '<i class=\"fas fa-times\"></i>';\n\n setTimeout(() => {\n button.innerHTML = '<i class=\"fas fa-copy\"></i>';\n }, 2000);\n }\n });\n\n // Add toolbar wrapper\n const toolbar = document.createElement('div');\n toolbar.className = 'code-toolbar';\n toolbar.appendChild(button);\n\n // Make pre position relative for absolute positioning of toolbar\n pre.style.position = 'relative';\n pre.appendChild(toolbar);\n });\n }\n\n /**\n * Initialize collapsible heading functionality\n * This method is a no-op - the component handles event binding\n * @param container The DOM element containing collapsible sections\n */\n public initializeCollapsibleHeadings(_container: HTMLElement): void {\n // Event binding is handled by the component's setupCollapsibleListeners\n // This method exists for API compatibility but does nothing\n }\n\n /**\n * Get the list of headings from the last parsed document\n * Useful for building table of contents\n */\n public getHeadingList(): HeadingInfo[] {\n return this.headingList;\n }\n\n /**\n * Get the current configuration\n */\n public getConfig(): ResolvedMarkdownConfig {\n return { ...this.currentConfig };\n }\n\n /**\n * Reset configuration to defaults\n */\n public resetConfig(): void {\n this.configureMarked(DEFAULT_MARKDOWN_CONFIG);\n }\n\n /**\n * Check if a language is supported by Prism\n */\n public isLanguageSupported(lang: string): boolean {\n return !!Prism.languages[lang];\n }\n\n /**\n * Get list of supported Prism languages\n */\n public getSupportedLanguages(): string[] {\n return Object.keys(Prism.languages).filter(\n lang => typeof Prism.languages[lang] === 'object'\n );\n }\n\n /**\n * Escape HTML entities for safe display\n */\n private escapeHtml(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n}\n"]}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the markdown component
|
|
3
|
+
*/
|
|
4
|
+
export interface MarkdownConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Enable Prism.js syntax highlighting for code blocks
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
enableHighlight?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Enable Mermaid diagram rendering
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
enableMermaid?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Enable copy-to-clipboard button on code blocks
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
enableCodeCopy?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Enable collapsible heading sections
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
enableCollapsibleHeadings?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Heading level at which to start collapsing (1-6)
|
|
27
|
+
* Only applies when enableCollapsibleHeadings is true
|
|
28
|
+
* @default 2
|
|
29
|
+
*/
|
|
30
|
+
collapsibleHeadingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
|
|
31
|
+
/**
|
|
32
|
+
* Whether collapsible sections should be expanded by default
|
|
33
|
+
* @default true
|
|
34
|
+
*/
|
|
35
|
+
collapsibleDefaultExpanded?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Specify which heading levels should start expanded.
|
|
38
|
+
* Array of heading levels (2-6) that should be expanded by default.
|
|
39
|
+
* Takes precedence over collapsibleDefaultExpanded for specified levels.
|
|
40
|
+
*
|
|
41
|
+
* Examples:
|
|
42
|
+
* - [2] = Only h2 expanded, h3-h6 collapsed
|
|
43
|
+
* - [2, 3] = h2 and h3 expanded, h4-h6 collapsed
|
|
44
|
+
* - [] = All collapsed (same as collapsibleDefaultExpanded: false)
|
|
45
|
+
* - undefined = Uses collapsibleDefaultExpanded for all levels
|
|
46
|
+
*
|
|
47
|
+
* @default undefined (uses collapsibleDefaultExpanded)
|
|
48
|
+
*/
|
|
49
|
+
autoExpandLevels?: number[];
|
|
50
|
+
/**
|
|
51
|
+
* Enable GitHub-style alerts ([!NOTE], [!WARNING], etc.)
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
enableAlerts?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Enable smartypants for typography (curly quotes, em/en dashes, ellipses)
|
|
57
|
+
* Converts:
|
|
58
|
+
* - "quotes" to "curly quotes"
|
|
59
|
+
* - -- to en-dash (–)
|
|
60
|
+
* - --- to em-dash (—)
|
|
61
|
+
* - ... to ellipsis (…)
|
|
62
|
+
* @default true
|
|
63
|
+
*/
|
|
64
|
+
enableSmartypants?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Enable SVG code block rendering
|
|
67
|
+
* When enabled, ```svg code blocks are rendered as actual SVG images
|
|
68
|
+
* @default true
|
|
69
|
+
*/
|
|
70
|
+
enableSvgRenderer?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Enable raw HTML passthrough in markdown content.
|
|
73
|
+
* When enabled, HTML tags in the markdown are rendered as actual HTML
|
|
74
|
+
* instead of being sanitized/stripped.
|
|
75
|
+
*
|
|
76
|
+
* Note: Even with enableHtml=true, scripts and event handlers are stripped
|
|
77
|
+
* unless enableJavaScript is also true.
|
|
78
|
+
*
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
enableHtml?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Enable JavaScript execution in HTML content.
|
|
84
|
+
* When enabled, <script> tags and on* event handlers are allowed.
|
|
85
|
+
*
|
|
86
|
+
* WARNING: This is a major security risk. Only enable for fully trusted content.
|
|
87
|
+
* In most cases, you want enableHtml=true with enableJavaScript=false.
|
|
88
|
+
*
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
enableJavaScript?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Enable GitHub-style heading IDs for anchor links
|
|
94
|
+
* @default true
|
|
95
|
+
*/
|
|
96
|
+
enableHeadingIds?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Prefix for heading IDs to avoid conflicts
|
|
99
|
+
* @default ''
|
|
100
|
+
*/
|
|
101
|
+
headingIdPrefix?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Enable line numbers in code blocks
|
|
104
|
+
* @default false
|
|
105
|
+
*/
|
|
106
|
+
enableLineNumbers?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Custom CSS class to apply to the markdown container
|
|
109
|
+
*/
|
|
110
|
+
containerClass?: string;
|
|
111
|
+
/**
|
|
112
|
+
* Prism.js theme to use (must be loaded in angular.json or via CSS import)
|
|
113
|
+
* Common themes: 'prism', 'prism-dark', 'prism-okaidia', 'prism-tomorrow', 'prism-coy'
|
|
114
|
+
*/
|
|
115
|
+
prismTheme?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Mermaid theme configuration
|
|
118
|
+
* @default 'default'
|
|
119
|
+
*/
|
|
120
|
+
mermaidTheme?: 'default' | 'dark' | 'forest' | 'neutral' | 'base';
|
|
121
|
+
/**
|
|
122
|
+
* Whether to sanitize HTML output
|
|
123
|
+
* Set to false only if you trust the markdown source completely
|
|
124
|
+
* @default true
|
|
125
|
+
*/
|
|
126
|
+
sanitize?: boolean;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Default configuration values
|
|
130
|
+
*/
|
|
131
|
+
export declare const DEFAULT_MARKDOWN_CONFIG: Required<Omit<MarkdownConfig, 'autoExpandLevels'>> & {
|
|
132
|
+
autoExpandLevels?: number[];
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Event emitted when markdown rendering is complete
|
|
136
|
+
*/
|
|
137
|
+
export interface MarkdownRenderEvent {
|
|
138
|
+
/**
|
|
139
|
+
* The rendered HTML string
|
|
140
|
+
*/
|
|
141
|
+
html: string;
|
|
142
|
+
/**
|
|
143
|
+
* Time taken to render in milliseconds
|
|
144
|
+
*/
|
|
145
|
+
renderTime: number;
|
|
146
|
+
/**
|
|
147
|
+
* Whether mermaid diagrams were rendered
|
|
148
|
+
*/
|
|
149
|
+
hasMermaid: boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Whether code blocks were highlighted
|
|
152
|
+
*/
|
|
153
|
+
hasCodeBlocks: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* List of heading IDs generated (for TOC building)
|
|
156
|
+
*/
|
|
157
|
+
headingIds: HeadingInfo[];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Information about a heading in the document
|
|
161
|
+
*/
|
|
162
|
+
export interface HeadingInfo {
|
|
163
|
+
/**
|
|
164
|
+
* The heading ID (for anchor links)
|
|
165
|
+
*/
|
|
166
|
+
id: string;
|
|
167
|
+
/**
|
|
168
|
+
* The heading text content
|
|
169
|
+
*/
|
|
170
|
+
text: string;
|
|
171
|
+
/**
|
|
172
|
+
* The heading level (1-6)
|
|
173
|
+
*/
|
|
174
|
+
level: number;
|
|
175
|
+
/**
|
|
176
|
+
* The raw markdown text
|
|
177
|
+
*/
|
|
178
|
+
raw: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Alert types supported by marked-alert
|
|
182
|
+
*/
|
|
183
|
+
export type AlertType = 'note' | 'tip' | 'important' | 'warning' | 'caution';
|
|
184
|
+
/**
|
|
185
|
+
* Configuration for a custom alert variant
|
|
186
|
+
*/
|
|
187
|
+
export interface AlertVariant {
|
|
188
|
+
type: string;
|
|
189
|
+
icon: string;
|
|
190
|
+
titleClassName?: string;
|
|
191
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default configuration values
|
|
3
|
+
*/
|
|
4
|
+
export const DEFAULT_MARKDOWN_CONFIG = {
|
|
5
|
+
enableHighlight: true,
|
|
6
|
+
enableMermaid: true,
|
|
7
|
+
enableCodeCopy: true,
|
|
8
|
+
enableCollapsibleHeadings: false,
|
|
9
|
+
collapsibleHeadingLevel: 2,
|
|
10
|
+
collapsibleDefaultExpanded: true,
|
|
11
|
+
autoExpandLevels: undefined,
|
|
12
|
+
enableAlerts: true,
|
|
13
|
+
enableSmartypants: true,
|
|
14
|
+
enableSvgRenderer: true,
|
|
15
|
+
enableHtml: false,
|
|
16
|
+
enableJavaScript: false,
|
|
17
|
+
enableHeadingIds: true,
|
|
18
|
+
headingIdPrefix: '',
|
|
19
|
+
enableLineNumbers: false,
|
|
20
|
+
containerClass: '',
|
|
21
|
+
prismTheme: 'prism-okaidia',
|
|
22
|
+
mermaidTheme: 'default',
|
|
23
|
+
sanitize: true
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=markdown.types.js.map
|