@mgks/docmd 0.2.7 → 0.2.8

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,94 @@
1
+ // Source file from the docmd project — https://github.com/mgks/docmd
2
+
3
+ const containers = {
4
+ card: {
5
+ name: 'card',
6
+ render: (tokens, idx) => {
7
+ if (tokens[idx].nesting === 1) {
8
+ const title = tokens[idx].info ? tokens[idx].info.trim() : '';
9
+ return `<div class="docmd-container card">${title ? `<div class="card-title">${title}</div>` : ''}<div class="card-content">`;
10
+ }
11
+ return '</div></div>';
12
+ }
13
+ },
14
+ callout: {
15
+ name: 'callout',
16
+ render: (tokens, idx) => {
17
+ if (tokens[idx].nesting === 1) {
18
+ const [type, ...titleParts] = tokens[idx].info.split(' ');
19
+ const title = titleParts.join(' ');
20
+ return `<div class="docmd-container callout callout-${type}">${title ? `<div class="callout-title">${title}</div>` : ''}<div class="callout-content">`;
21
+ }
22
+ return '</div></div>';
23
+ }
24
+ },
25
+ button: {
26
+ name: 'button',
27
+ selfClosing: true,
28
+ render: (tokens, idx) => {
29
+ if (tokens[idx].nesting === 1) {
30
+ const parts = tokens[idx].info.split(' ');
31
+ const text = parts[0];
32
+ const url = parts[1];
33
+ const color = parts[2];
34
+ const colorStyle = color && color.startsWith('color:') ? ` style="background-color: ${color.split(':')[1]}"` : '';
35
+
36
+ let finalUrl = url;
37
+ let targetAttr = '';
38
+ if (url && url.startsWith('external:')) {
39
+ finalUrl = url.substring(9);
40
+ targetAttr = ' target="_blank" rel="noopener noreferrer"';
41
+ }
42
+
43
+ return `<a href="${finalUrl}" class="docmd-button"${colorStyle}${targetAttr}>${text.replace(/_/g, ' ')}</a>`;
44
+ }
45
+ return '';
46
+ }
47
+ },
48
+ steps: {
49
+ name: 'steps',
50
+ render: (tokens, idx) => {
51
+ if (tokens[idx].nesting === 1) {
52
+ return '<div class="docmd-container steps steps-reset steps-numbering">';
53
+ }
54
+ return '</div>';
55
+ }
56
+ },
57
+ collapsible: {
58
+ name: 'collapsible',
59
+ render: (tokens, idx) => {
60
+ if (tokens[idx].nesting === 1) {
61
+ const info = tokens[idx].info.trim();
62
+ let isOpen = false;
63
+ let displayTitle = info;
64
+
65
+ if (info.startsWith('open ')) {
66
+ isOpen = true;
67
+ displayTitle = info.substring(5);
68
+ }
69
+ if (!displayTitle) displayTitle = 'Click to expand';
70
+
71
+ return `<details class="docmd-container collapsible" ${isOpen ? 'open' : ''}>
72
+ <summary class="collapsible-summary">
73
+ <span class="collapsible-title">${displayTitle}</span>
74
+ <span class="collapsible-arrow">
75
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"/></svg>
76
+ </span>
77
+ </summary>
78
+ <div class="collapsible-content">`;
79
+ }
80
+ return '</div></details>\n';
81
+ }
82
+ },
83
+ changelog: {
84
+ name: 'changelog',
85
+ render: (tokens, idx) => {
86
+ if (tokens[idx].nesting === 1) {
87
+ return '<div class="docmd-container changelog-timeline">';
88
+ }
89
+ return '</div>';
90
+ }
91
+ }
92
+ };
93
+
94
+ module.exports = { containers };
@@ -0,0 +1,90 @@
1
+ // Source file from the docmd project — https://github.com/mgks/docmd
2
+
3
+ const customOrderedListOpenRenderer = function(tokens, idx, options, env, self) {
4
+ const token = tokens[idx];
5
+ let isInSteps = false;
6
+ for (let i = idx - 1; i >= 0; i--) {
7
+ if (tokens[i].type === 'container_steps_open') {
8
+ isInSteps = true;
9
+ break;
10
+ }
11
+ if (tokens[i].type === 'container_steps_close') {
12
+ break;
13
+ }
14
+ }
15
+ if (isInSteps) {
16
+ const start = token.attrGet('start');
17
+ return start ? `<ol class="steps-list" start="${start}">` : '<ol class="steps-list">';
18
+ }
19
+ const start = token.attrGet('start');
20
+ return start ? `<ol start="${start}">` : '<ol>';
21
+ };
22
+
23
+ const customListItemOpenRenderer = function(tokens, idx, options, env, self) {
24
+ const token = tokens[idx];
25
+ let isInStepsList = false;
26
+ for (let i = idx - 1; i >= 0; i--) {
27
+ if (tokens[i].type === 'ordered_list_open' && tokens[i].markup && tokens[i].level < token.level) {
28
+ let j = i - 1;
29
+ while (j >= 0) {
30
+ if (tokens[j].type === 'container_steps_open') {
31
+ isInStepsList = true;
32
+ break;
33
+ }
34
+ if (tokens[j].type === 'container_steps_close') {
35
+ break;
36
+ }
37
+ j--;
38
+ }
39
+ break;
40
+ }
41
+ }
42
+ if (isInStepsList) {
43
+ return '<li class="step-item">';
44
+ }
45
+ return '<li>';
46
+ };
47
+
48
+ const customImageRenderer = function(tokens, idx, options, env, self) {
49
+ const defaultImageRenderer = function(tokens, idx, options, env, self) { return self.renderToken(tokens, idx, options); };
50
+ const renderedImage = defaultImageRenderer(tokens, idx, options, env, self);
51
+ const nextToken = tokens[idx + 1];
52
+ if (nextToken && nextToken.type === 'attrs_block') {
53
+ const attrs = nextToken.attrs || [];
54
+ const attrsStr = attrs.map(([name, value]) => `${name}="${value}"`).join(' ');
55
+ return renderedImage.replace('<img ', `<img ${attrsStr} `);
56
+ }
57
+ return renderedImage;
58
+ };
59
+
60
+ // Table Wrapper for horizontal scrolling
61
+ const tableOpenRenderer = (tokens, idx, options, env, self) => '<div class="table-wrapper">' + self.renderToken(tokens, idx, options);
62
+ const tableCloseRenderer = (tokens, idx, options, env, self) => self.renderToken(tokens, idx, options) + '</div>';
63
+
64
+ // Tabs Renderers
65
+ const tabsOpenRenderer = (tokens, idx) => `<div class="${tokens[idx].attrs.map(attr => attr[1]).join(' ')}">`;
66
+ const tabsNavOpenRenderer = () => '<div class="docmd-tabs-nav">';
67
+ const tabsNavCloseRenderer = () => '</div>';
68
+ const tabsNavItemRenderer = (tokens, idx) => `<div class="${tokens[idx].attrs[0][1]}">${tokens[idx].content}</div>`;
69
+ const tabsContentOpenRenderer = () => '<div class="docmd-tabs-content">';
70
+ const tabsContentCloseRenderer = () => '</div>';
71
+ const tabPaneOpenRenderer = (tokens, idx) => `<div class="${tokens[idx].attrs[0][1]}">`;
72
+ const tabPaneCloseRenderer = () => '</div>';
73
+ const tabsCloseRenderer = () => '</div>';
74
+
75
+ module.exports = {
76
+ customOrderedListOpenRenderer,
77
+ customListItemOpenRenderer,
78
+ customImageRenderer,
79
+ tableOpenRenderer,
80
+ tableCloseRenderer,
81
+ tabsOpenRenderer,
82
+ tabsNavOpenRenderer,
83
+ tabsNavCloseRenderer,
84
+ tabsNavItemRenderer,
85
+ tabsContentOpenRenderer,
86
+ tabsContentCloseRenderer,
87
+ tabPaneOpenRenderer,
88
+ tabPaneCloseRenderer,
89
+ tabsCloseRenderer
90
+ };
@@ -0,0 +1,355 @@
1
+ // Source file from the docmd project — https://github.com/mgks/docmd
2
+
3
+ const MarkdownIt = require('markdown-it'); // Required for inner rendering fallback logic
4
+ const { containers } = require('./containers');
5
+
6
+ // --- Helper: Create isolated parser for tabs (prevents recursion stack issues) ---
7
+ // Note: We pass a callback to get the main config logic from setup.js later if needed,
8
+ // but for now we reconstruct a basic one for tab/changelog internals to ensure plugins run.
9
+ // Ideally, we prefer reusing state.md.render() where possible.
10
+
11
+ // --- 1. Advanced Container Rule (Nesting Logic) ---
12
+ function advancedContainerRule(state, startLine, endLine, silent) {
13
+ const start = state.bMarks[startLine] + state.tShift[startLine];
14
+ const max = state.eMarks[startLine];
15
+ const lineContent = state.src.slice(start, max).trim();
16
+
17
+ const containerMatch = lineContent.match(/^:::\s*(\w+)(?:\s+(.+))?$/);
18
+ if (!containerMatch) return false;
19
+
20
+ const [, containerName, params] = containerMatch;
21
+ const container = containers[containerName];
22
+ if (!container) return false;
23
+ if (silent) return true;
24
+
25
+ if (container.selfClosing) {
26
+ const openToken = state.push(`container_${containerName}_open`, 'div', 1);
27
+ openToken.info = params || '';
28
+ const closeToken = state.push(`container_${containerName}_close`, 'div', -1);
29
+ state.line = startLine + 1;
30
+ return true;
31
+ }
32
+
33
+ let nextLine = startLine;
34
+ let found = false;
35
+ let depth = 1;
36
+
37
+ while (nextLine < endLine) {
38
+ nextLine++;
39
+ const nextStart = state.bMarks[nextLine] + state.tShift[nextLine];
40
+ const nextMax = state.eMarks[nextLine];
41
+ const nextContent = state.src.slice(nextStart, nextMax).trim();
42
+
43
+ if (nextContent.startsWith(':::')) {
44
+ const containerMatch = nextContent.match(/^:::\s*(\w+)/);
45
+ if (containerMatch && containerMatch[1] !== containerName) {
46
+ const innerContainer = containers[containerMatch[1]];
47
+ if (innerContainer && innerContainer.render && !innerContainer.selfClosing) {
48
+ depth++;
49
+ }
50
+ continue;
51
+ }
52
+ }
53
+
54
+ if (nextContent === ':::') {
55
+ depth--;
56
+ if (depth === 0) {
57
+ found = true;
58
+ break;
59
+ }
60
+ }
61
+ }
62
+
63
+ if (!found) return false;
64
+
65
+ const openToken = state.push(`container_${containerName}_open`, 'div', 1);
66
+ openToken.info = params || '';
67
+
68
+ const oldParentType = state.parentType;
69
+ const oldLineMax = state.lineMax;
70
+ state.parentType = 'container';
71
+ state.lineMax = nextLine;
72
+
73
+ state.md.block.tokenize(state, startLine + 1, nextLine);
74
+
75
+ const closeToken = state.push(`container_${containerName}_close`, 'div', -1);
76
+
77
+ state.parentType = oldParentType;
78
+ state.lineMax = oldLineMax;
79
+ state.line = nextLine + 1;
80
+
81
+ return true;
82
+ }
83
+
84
+ // --- 2. Changelog Timeline Rule (FIXED FOR NESTING) ---
85
+ function changelogTimelineRule(state, startLine, endLine, silent) {
86
+ const start = state.bMarks[startLine] + state.tShift[startLine];
87
+ const max = state.eMarks[startLine];
88
+ const lineContent = state.src.slice(start, max).trim();
89
+
90
+ if (lineContent !== '::: changelog') return false;
91
+ if (silent) return true;
92
+
93
+ let nextLine = startLine;
94
+ let found = false;
95
+ let depth = 1;
96
+
97
+ while (nextLine < endLine) {
98
+ nextLine++;
99
+ const nextStart = state.bMarks[nextLine] + state.tShift[nextLine];
100
+ const nextMax = state.eMarks[nextLine];
101
+ const nextContent = state.src.slice(nextStart, nextMax).trim();
102
+
103
+ if (nextContent.startsWith(':::')) {
104
+ const match = nextContent.match(/^:::\s*(\w+)/);
105
+ if (match) {
106
+ const containerName = match[1];
107
+ // Don't count self-closing like buttons for depth
108
+ const containerDef = containers[containerName];
109
+ if (!containerDef || !containerDef.selfClosing) {
110
+ depth++;
111
+ }
112
+ }
113
+ }
114
+
115
+ if (nextContent === ':::') {
116
+ depth--;
117
+ if (depth === 0) { found = true; break; }
118
+ }
119
+ }
120
+
121
+ if (!found) return false;
122
+
123
+ let content = '';
124
+ for (let i = startLine + 1; i < nextLine; i++) {
125
+ const lineStart = state.bMarks[i] + state.tShift[i];
126
+ const lineEnd = state.eMarks[i];
127
+ content += state.src.slice(lineStart, lineEnd) + '\n';
128
+ }
129
+
130
+ const lines = content.split('\n');
131
+ const entries = [];
132
+ let currentEntry = null;
133
+ let currentContent = [];
134
+
135
+ for (let i = 0; i < lines.length; i++) {
136
+ const line = lines[i].trim();
137
+ const markerMatch = line.match(/^==\s+(.+)$/);
138
+
139
+ if (markerMatch) {
140
+ if (currentEntry) {
141
+ currentEntry.content = currentContent.join('\n');
142
+ entries.push(currentEntry);
143
+ }
144
+ currentEntry = { meta: markerMatch[1], content: '' };
145
+ currentContent = [];
146
+ } else if (currentEntry) {
147
+ currentContent.push(lines[i]);
148
+ }
149
+ }
150
+ if (currentEntry) {
151
+ currentEntry.content = currentContent.join('\n');
152
+ entries.push(currentEntry);
153
+ }
154
+
155
+ state.push('container_changelog_open', 'div', 1);
156
+
157
+ entries.forEach(entry => {
158
+ const entryOpen = state.push('html_block', '', 0);
159
+ entryOpen.content = `<div class="changelog-entry">
160
+ <div class="changelog-meta"><span class="changelog-date">${entry.meta}</span></div>
161
+ <div class="changelog-body">`;
162
+
163
+ // --- FIX: Use parent parser to render inner content ---
164
+ // This ensures callouts/cards inside changelogs are parsed
165
+ entryOpen.content += state.md.render(entry.content, state.env);
166
+
167
+ const entryClose = state.push('html_block', '', 0);
168
+ entryClose.content = `</div></div>`;
169
+ });
170
+
171
+ state.push('container_changelog_close', 'div', -1);
172
+ state.line = nextLine + 1;
173
+ return true;
174
+ }
175
+
176
+ // --- 3. Steps Container Rule ---
177
+ function stepsContainerRule(state, startLine, endLine, silent) {
178
+ const start = state.bMarks[startLine] + state.tShift[startLine];
179
+ const max = state.eMarks[startLine];
180
+ const lineContent = state.src.slice(start, max).trim();
181
+ if (lineContent !== '::: steps') return false;
182
+ if (silent) return true;
183
+
184
+ let nextLine = startLine;
185
+ let found = false;
186
+ let depth = 1;
187
+
188
+ while (nextLine < endLine) {
189
+ nextLine++;
190
+ const nextStart = state.bMarks[nextLine] + state.tShift[nextLine];
191
+ const nextMax = state.eMarks[nextLine];
192
+ const nextContent = state.src.slice(nextStart, nextMax).trim();
193
+
194
+ if (nextContent.startsWith('== tab')) { continue; }
195
+
196
+ if (nextContent.startsWith(':::')) {
197
+ const containerMatch = nextContent.match(/^:::\s*(\w+)/);
198
+ if (containerMatch) {
199
+ const containerName = containerMatch[1];
200
+ const innerContainer = containers[containerName];
201
+ if (innerContainer && !innerContainer.selfClosing) {
202
+ depth++;
203
+ }
204
+ continue;
205
+ }
206
+ }
207
+
208
+ if (nextContent === ':::') {
209
+ depth--;
210
+ if (depth === 0) { found = true; break; }
211
+ }
212
+ }
213
+
214
+ if (!found) return false;
215
+
216
+ const openToken = state.push('container_steps_open', 'div', 1);
217
+ openToken.info = '';
218
+
219
+ const oldParentType = state.parentType;
220
+ const oldLineMax = state.lineMax;
221
+ state.parentType = 'container';
222
+ state.lineMax = nextLine;
223
+ state.md.block.tokenize(state, startLine + 1, nextLine);
224
+ const closeToken = state.push('container_steps_close', 'div', -1);
225
+ state.parentType = oldParentType;
226
+ state.lineMax = oldLineMax;
227
+ state.line = nextLine + 1;
228
+ return true;
229
+ }
230
+
231
+ // --- 4. Enhanced Tabs Rule ---
232
+ function enhancedTabsRule(state, startLine, endLine, silent) {
233
+ const start = state.bMarks[startLine] + state.tShift[startLine];
234
+ const max = state.eMarks[startLine];
235
+ const lineContent = state.src.slice(start, max).trim();
236
+
237
+ if (lineContent !== '::: tabs') return false;
238
+ if (silent) return true;
239
+
240
+ let nextLine = startLine;
241
+ let found = false;
242
+ let depth = 1;
243
+ while (nextLine < endLine) {
244
+ nextLine++;
245
+ const nextStart = state.bMarks[nextLine] + state.tShift[nextLine];
246
+ const nextMax = state.eMarks[nextLine];
247
+ const nextContent = state.src.slice(nextStart, nextMax).trim();
248
+
249
+ if (nextContent.startsWith(':::')) {
250
+ const containerMatch = nextContent.match(/^:::\s*(\w+)/);
251
+ if (containerMatch && containerMatch[1] !== 'tabs') {
252
+ if (containerMatch[1] === 'steps') continue;
253
+ const innerContainer = containers[containerMatch[1]];
254
+ if (innerContainer && !innerContainer.selfClosing) depth++;
255
+ continue;
256
+ }
257
+ }
258
+
259
+ if (nextContent === ':::') {
260
+ depth--;
261
+ if (depth === 0) { found = true; break; }
262
+ }
263
+ }
264
+ if (!found) return false;
265
+
266
+ let content = '';
267
+ for (let i = startLine + 1; i < nextLine; i++) {
268
+ const lineStart = state.bMarks[i] + state.tShift[i];
269
+ const lineEnd = state.eMarks[i];
270
+ content += state.src.slice(lineStart, lineEnd) + '\n';
271
+ }
272
+
273
+ const lines = content.split('\n');
274
+ const tabs = [];
275
+ let currentTab = null;
276
+ let currentContent = [];
277
+
278
+ for (let i = 0; i < lines.length; i++) {
279
+ const line = lines[i].trim();
280
+ const tabMatch = line.match(/^==\s*tab\s+(?:"([^"]+)"|(\S+))$/);
281
+
282
+ if (tabMatch) {
283
+ if (currentTab) {
284
+ currentTab.content = currentContent.join('\n').trim();
285
+ tabs.push(currentTab);
286
+ }
287
+ const title = tabMatch[1] || tabMatch[2];
288
+ currentTab = { title: title, content: '' };
289
+ currentContent = [];
290
+ } else if (currentTab) {
291
+ if (lines[i].trim() && !lines[i].trim().startsWith('==')) {
292
+ currentContent.push(lines[i]);
293
+ }
294
+ }
295
+ }
296
+ if (currentTab) {
297
+ currentTab.content = currentContent.join('\n').trim();
298
+ tabs.push(currentTab);
299
+ }
300
+
301
+ const openToken = state.push('tabs_open', 'div', 1);
302
+ openToken.attrs = [['class', 'docmd-tabs']];
303
+
304
+ const navToken = state.push('tabs_nav_open', 'div', 1);
305
+ navToken.attrs = [['class', 'docmd-tabs-nav']];
306
+ tabs.forEach((tab, index) => {
307
+ const navItemToken = state.push('tabs_nav_item', 'div', 0);
308
+ navItemToken.attrs = [['class', `docmd-tabs-nav-item ${index === 0 ? 'active' : ''}`]];
309
+ navItemToken.content = tab.title;
310
+ });
311
+ state.push('tabs_nav_close', 'div', -1);
312
+
313
+ const contentToken = state.push('tabs_content_open', 'div', 1);
314
+ contentToken.attrs = [['class', 'docmd-tabs-content']];
315
+ tabs.forEach((tab, index) => {
316
+ const paneToken = state.push('tab_pane_open', 'div', 1);
317
+ paneToken.attrs = [['class', `docmd-tab-pane ${index === 0 ? 'active' : ''}`]];
318
+
319
+ if (tab.content.trim()) {
320
+ // Use recursion here if possible, or create basic instance to prevent circular dep issues in this modular file
321
+ // Since we are inside rules.js, we rely on state.md being available or fallback to simple render
322
+ const renderedContent = state.md.render(tab.content.trim(), state.env);
323
+ const htmlToken = state.push('html_block', '', 0);
324
+ htmlToken.content = renderedContent;
325
+ }
326
+
327
+ state.push('tab_pane_close', 'div', -1);
328
+ });
329
+ state.push('tabs_content_close', 'div', -1);
330
+ state.push('tabs_close', 'div', -1);
331
+ state.line = nextLine + 1;
332
+ return true;
333
+ }
334
+
335
+ // --- 5. Standalone Closing Rule ---
336
+ const standaloneClosingRule = (state, startLine, endLine, silent) => {
337
+ const start = state.bMarks[startLine] + state.tShift[startLine];
338
+ const max = state.eMarks[startLine];
339
+ const lineContent = state.src.slice(start, max).trim();
340
+
341
+ if (lineContent === ':::') {
342
+ if (silent) return true;
343
+ state.line = startLine + 1;
344
+ return true;
345
+ }
346
+ return false;
347
+ };
348
+
349
+ module.exports = {
350
+ advancedContainerRule,
351
+ changelogTimelineRule,
352
+ stepsContainerRule,
353
+ enhancedTabsRule,
354
+ standaloneClosingRule
355
+ };
@@ -0,0 +1,108 @@
1
+ // Source file from the docmd project — https://github.com/mgks/docmd
2
+
3
+ const MarkdownIt = require('markdown-it');
4
+ const hljs = require('highlight.js');
5
+ const attrs = require('markdown-it-attrs');
6
+ const markdown_it_footnote = require('markdown-it-footnote');
7
+ const markdown_it_task_lists = require('markdown-it-task-lists');
8
+ const markdown_it_abbr = require('markdown-it-abbr');
9
+ const markdown_it_deflist = require('markdown-it-deflist');
10
+
11
+ const { containers } = require('./containers');
12
+ const rules = require('./rules');
13
+ const renderers = require('./renderers');
14
+
15
+ // Custom plugin for Heading IDs
16
+ const headingIdPlugin = (md) => {
17
+ const originalHeadingOpen = md.renderer.rules.heading_open || function(tokens, idx, options, env, self) {
18
+ return self.renderToken(tokens, idx, options);
19
+ };
20
+ md.renderer.rules.heading_open = function(tokens, idx, options, env, self) {
21
+ const token = tokens[idx];
22
+ const contentToken = tokens[idx + 1];
23
+ if (contentToken && contentToken.type === 'inline' && contentToken.content) {
24
+ const headingText = contentToken.content;
25
+ const id = headingText
26
+ .toLowerCase()
27
+ .replace(/\s+/g, '-')
28
+ .replace(/[^\w-]+/g, '')
29
+ .replace(/--+/g, '-')
30
+ .replace(/^-+/, '')
31
+ .replace(/-+$/, '');
32
+ if (id) token.attrSet('id', id);
33
+ }
34
+ return originalHeadingOpen(tokens, idx, options, env, self);
35
+ };
36
+ };
37
+
38
+ function createMarkdownItInstance(config) {
39
+ const mdOptions = {
40
+ html: true,
41
+ linkify: true,
42
+ typographer: true,
43
+ breaks: true,
44
+ };
45
+
46
+ const highlightFn = (str, lang) => {
47
+ if (lang === 'mermaid') {
48
+ return `<pre class="mermaid">${new MarkdownIt().utils.escapeHtml(str)}</pre>`;
49
+ }
50
+ if (lang && hljs.getLanguage(lang)) {
51
+ try {
52
+ return `<pre class="hljs"><code>${hljs.highlight(str, { language: lang, ignoreIllegals: true }).value}</code></pre>`;
53
+ } catch (e) { console.error(e); }
54
+ }
55
+ return `<pre class="hljs"><code>${new MarkdownIt().utils.escapeHtml(str)}</code></pre>`;
56
+ };
57
+
58
+ mdOptions.highlight = config.theme?.codeHighlight !== false ? highlightFn : (str, lang) => {
59
+ if (lang === 'mermaid') return `<pre class="mermaid">${new MarkdownIt().utils.escapeHtml(str)}</pre>`;
60
+ return `<pre><code>${new MarkdownIt().utils.escapeHtml(str)}</code></pre>`;
61
+ };
62
+
63
+ const md = new MarkdownIt(mdOptions);
64
+
65
+ // Plugins
66
+ md.use(attrs, { leftDelimiter: '{', rightDelimiter: '}' });
67
+ md.use(markdown_it_footnote);
68
+ md.use(markdown_it_task_lists);
69
+ md.use(markdown_it_abbr);
70
+ md.use(markdown_it_deflist);
71
+ md.use(headingIdPlugin);
72
+
73
+ // Register Containers
74
+ Object.keys(containers).forEach(containerName => {
75
+ const container = containers[containerName];
76
+ md.renderer.rules[`container_${containerName}_open`] = container.render;
77
+ md.renderer.rules[`container_${containerName}_close`] = container.render;
78
+ });
79
+
80
+ // Register Rules (Order Matters)
81
+ md.block.ruler.before('fence', 'steps_container', rules.stepsContainerRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
82
+ md.block.ruler.before('fence', 'enhanced_tabs', rules.enhancedTabsRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
83
+ md.block.ruler.before('fence', 'changelog_timeline', rules.changelogTimelineRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
84
+ md.block.ruler.before('paragraph', 'advanced_container', rules.advancedContainerRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
85
+ md.block.ruler.before('paragraph', 'standalone_closing', rules.standaloneClosingRule, { alt: ['paragraph', 'reference', 'blockquote', 'list'] });
86
+
87
+ // Register Renderers
88
+ md.renderer.rules.ordered_list_open = renderers.customOrderedListOpenRenderer;
89
+ md.renderer.rules.list_item_open = renderers.customListItemOpenRenderer;
90
+ md.renderer.rules.image = renderers.customImageRenderer;
91
+ md.renderer.rules.table_open = renderers.tableOpenRenderer;
92
+ md.renderer.rules.table_close = renderers.tableCloseRenderer;
93
+
94
+ // Tabs Renderers
95
+ md.renderer.rules.tabs_open = renderers.tabsOpenRenderer;
96
+ md.renderer.rules.tabs_nav_open = renderers.tabsNavOpenRenderer;
97
+ md.renderer.rules.tabs_nav_close = renderers.tabsNavCloseRenderer;
98
+ md.renderer.rules.tabs_nav_item = renderers.tabsNavItemRenderer;
99
+ md.renderer.rules.tabs_content_open = renderers.tabsContentOpenRenderer;
100
+ md.renderer.rules.tabs_content_close = renderers.tabsContentCloseRenderer;
101
+ md.renderer.rules.tab_pane_open = renderers.tabPaneOpenRenderer;
102
+ md.renderer.rules.tab_pane_close = renderers.tabPaneCloseRenderer;
103
+ md.renderer.rules.tabs_close = renderers.tabsCloseRenderer;
104
+
105
+ return md;
106
+ }
107
+
108
+ module.exports = { createMarkdownItInstance };