mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-02 10:00:17 +05:30
feat: add markdown extensions (footnotes, admonitions, TOC)
This commit is contained in:
+64
-1
@@ -33,6 +33,39 @@ marked.use({
|
|||||||
gfm: true
|
gfm: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Footnotes support
|
||||||
|
const markedFootnote = require('marked-footnote');
|
||||||
|
marked.use(markedFootnote());
|
||||||
|
|
||||||
|
// Admonitions support (:::note, :::warning, :::tip, :::danger, :::info)
|
||||||
|
marked.use({
|
||||||
|
extensions: [{
|
||||||
|
name: 'admonition',
|
||||||
|
level: 'block',
|
||||||
|
start(src) { return src.match(/^:::(note|warning|tip|danger|info)/m)?.index; },
|
||||||
|
tokenizer(src) {
|
||||||
|
const match = src.match(/^:::(note|warning|tip|danger|info)\s*\n([\s\S]*?)^:::\s*$/m);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
type: 'admonition',
|
||||||
|
raw: match[0],
|
||||||
|
admonitionType: match[1],
|
||||||
|
text: match[2].trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
const icons = { note: '\u2139', warning: '\u26A0', tip: '\uD83D\uDCA1', danger: '\uD83D\uDD34', info: '\u2139' };
|
||||||
|
const icon = icons[token.admonitionType] || '\u2139';
|
||||||
|
const inner = this.parser.parse(token.text);
|
||||||
|
return `<div class="admonition admonition-${token.admonitionType}">
|
||||||
|
<div class="admonition-title">${icon} ${token.admonitionType.charAt(0).toUpperCase() + token.admonitionType.slice(1)}</div>
|
||||||
|
<div class="admonition-content">${inner}</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
// Tab Management
|
// Tab Management
|
||||||
class TabManager {
|
class TabManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -337,7 +370,37 @@ class TabManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const html = marked.parse(tab.content);
|
const html = marked.parse(tab.content);
|
||||||
const sanitizedHtml = DOMPurify.sanitize(html);
|
let sanitizedHtml = DOMPurify.sanitize(html);
|
||||||
|
|
||||||
|
// TOC generation
|
||||||
|
if (sanitizedHtml.includes('[[toc]]') || sanitizedHtml.includes('[TOC]')) {
|
||||||
|
const headingRegex = /<h([1-6])[^>]*>(.*?)<\/h[1-6]>/gi;
|
||||||
|
let tocMatch;
|
||||||
|
const toc = [];
|
||||||
|
const tempHtml = sanitizedHtml;
|
||||||
|
while ((tocMatch = headingRegex.exec(tempHtml)) !== null) {
|
||||||
|
const level = parseInt(tocMatch[1]);
|
||||||
|
const text = tocMatch[2].replace(/<[^>]+>/g, '');
|
||||||
|
const id = text.toLowerCase().replace(/[^\w]+/g, '-');
|
||||||
|
toc.push({ level, text, id });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toc.length > 0) {
|
||||||
|
const tocHtml = '<nav class="toc"><h2>Table of Contents</h2><ul>' +
|
||||||
|
toc.map(h => `<li class="toc-h${h.level}"><a href="#${h.id}">${h.text}</a></li>`).join('') +
|
||||||
|
'</ul></nav>';
|
||||||
|
sanitizedHtml = sanitizedHtml.replace(/\[\[toc\]\]|\[TOC\]/gi, tocHtml);
|
||||||
|
|
||||||
|
// Add IDs to headings for anchor links
|
||||||
|
toc.forEach(h => {
|
||||||
|
sanitizedHtml = sanitizedHtml.replace(
|
||||||
|
new RegExp(`(<h${h.level}[^>]*)>`, 'i'),
|
||||||
|
`$1 id="${h.id}">`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
preview.innerHTML = sanitizedHtml;
|
preview.innerHTML = sanitizedHtml;
|
||||||
|
|
||||||
// Render math expressions if KaTeX is available
|
// Render math expressions if KaTeX is available
|
||||||
|
|||||||
@@ -4009,5 +4009,43 @@ body.theme-github .mermaid {
|
|||||||
border-color: #e1e4e8;
|
border-color: #e1e4e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Task List Styles */
|
||||||
|
.preview-content input[type="checkbox"] {
|
||||||
|
margin-right: 6px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.preview-content li.task-list-item {
|
||||||
|
list-style: none;
|
||||||
|
margin-left: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Admonition Styles */
|
||||||
|
.admonition {
|
||||||
|
border-left: 4px solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
.admonition-title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.admonition-note, .admonition-info { border-color: #3b82f6; background: #eff6ff; }
|
||||||
|
.admonition-warning { border-color: #f59e0b; background: #fffbeb; }
|
||||||
|
.admonition-tip { border-color: #10b981; background: #ecfdf5; }
|
||||||
|
.admonition-danger { border-color: #ef4444; background: #fef2f2; }
|
||||||
|
|
||||||
|
/* TOC Styles */
|
||||||
|
.toc { background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; margin: 16px 0; }
|
||||||
|
.toc h2 { font-size: 14px; margin-bottom: 8px; }
|
||||||
|
.toc ul { list-style: none; padding: 0; }
|
||||||
|
.toc li { padding: 2px 0; }
|
||||||
|
.toc .toc-h1 { padding-left: 0; }
|
||||||
|
.toc .toc-h2 { padding-left: 16px; }
|
||||||
|
.toc .toc-h3 { padding-left: 32px; }
|
||||||
|
.toc .toc-h4 { padding-left: 48px; }
|
||||||
|
.toc a { color: #5661b3; text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
/* Command Palette Styles */
|
/* Command Palette Styles */
|
||||||
/* Command Palette styles moved to styles-modern.css */
|
/* Command Palette styles moved to styles-modern.css */
|
||||||
|
|||||||
Reference in New Issue
Block a user