+
Skip to content

(feat): added mermaid rendering support in markdown #489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ function defaultTask(cb) {
"public/script/external/katex.min.js",
"public/script/external/katex-auto-render.min.js",
"public/script/external/marked-katex-extension.umd.min.js",
"public/script/external/mermaid.min.js",
"public/script/external/marked-mermaid.js",
"public/script/external/notebook.min.js",
"public/script/external/org.js",
"public/script/external/jquery-3.4.1.min.js",
Expand All @@ -39,6 +41,7 @@ function defaultTask(cb) {
"public/css/font-awesome.min.css",
"public/css/notebook.css",
"public/css/katex.min.css",
"public/css/mermaid.css",
"public/css/github-markdown.min.css",
"public/css/style.css",
];
Expand Down
2 changes: 1 addition & 1 deletion public/css/all.min.css

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions public/css/mermaid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* Mermaid diagram styles */
.mermaid {
text-align: center;
margin: 1em 0;
overflow: auto;
}

/* Ensure diagrams are responsive */
.mermaid svg {
max-width: 100%;
height: auto;
}

/* Dark mode support for Mermaid diagrams */
.dark-mode .mermaid {
filter: invert(1) hue-rotate(180deg);
}

.dark-mode .mermaid svg {
background: transparent;
}

/* Ensure text remains readable in dark mode */
.dark-mode .mermaid .nodeLabel,
.dark-mode .mermaid .edgeLabel {
filter: invert(1) hue-rotate(180deg);
}

/* Custom styling for different diagram types */
.mermaid .node rect,
.mermaid .node circle,
.mermaid .node ellipse,
.mermaid .node polygon {
stroke-width: 2px;
}

/* Flowchart specific styles */
.mermaid .flowchart-link {
stroke-width: 2px;
}

/* Sequence diagram specific styles */
.mermaid .actor {
fill: #f9f9f9;
stroke: #333;
stroke-width: 2px;
}

.mermaid .messageLine0,
.mermaid .messageLine1 {
stroke: #333;
stroke-width: 1.5px;
}

/* Gantt chart specific styles */
.mermaid .section0,
.mermaid .section1,
.mermaid .section2,
.mermaid .section3 {
opacity: 0.8;
}

/* Pie chart specific styles */
.mermaid .pieChart .slice {
opacity: 0.9;
}

/* Make sure diagrams are visible in the markdown content area */
.markdown-body .mermaid {
background: transparent;
border: none;
padding: 10px;
}

/* Error handling for failed diagrams */
.mermaid .error {
color: #d32f2f;
font-family: monospace;
font-size: 12px;
padding: 10px;
border: 1px solid #d32f2f;
border-radius: 4px;
background-color: #ffebee;
}
2,619 changes: 2,617 additions & 2 deletions public/script/bundle.min.js

Large diffs are not rendered by default.

70 changes: 70 additions & 0 deletions public/script/external/marked-mermaid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Marked.js extension for Mermaid diagrams
function markedMermaid(options) {
options = options || {};

return {
extensions: [
{
name: 'mermaid',
level: 'block',
start(src) {
return src.match(/^```mermaid/m)?.index;
},
tokenizer(src, tokens) {
const rule = /^```mermaid\n([\s\S]*?)\n```/;
const match = rule.exec(src);
if (match) {
return {
type: 'mermaid',
raw: match[0],
text: match[1].trim()
};
}
},
renderer(token) {
const id = 'mermaid-' + Math.random().toString(36).substr(2, 9);

// Initialize Mermaid if not already done
if (typeof mermaid !== 'undefined' && !window.mermaidInitialized) {
mermaid.initialize({
startOnLoad: false,
theme: 'default',
securityLevel: 'loose'
});
window.mermaidInitialized = true;
}

// Create a div that will be processed by Mermaid
const div = `<div class="mermaid" id="${id}">${token.text}</div>`;

// Schedule Mermaid rendering for after DOM insertion
setTimeout(() => {
if (typeof mermaid !== 'undefined') {
try {
const element = document.getElementById(id);
if (element && !element.getAttribute('data-processed')) {
mermaid.init(undefined, element);
element.setAttribute('data-processed', 'true');
}
} catch (error) {
console.error('Mermaid rendering error:', error);
}
}
}, 0);

return div;
}
}
]
};
}

// Make it available globally
if (typeof window !== 'undefined') {
window.markedMermaid = markedMermaid;
}

// CommonJS/Node.js export
if (typeof module !== 'undefined' && module.exports) {
module.exports = markedMermaid;
}
2,659 changes: 2,659 additions & 0 deletions public/script/external/mermaid.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions public/script/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,6 @@ function renderMD(md, baseUrlValue) {
throwOnError: false,
})
);
marked.use(markedMermaid());
return marked.parse(md, { renderer });
}
176 changes: 176 additions & 0 deletions test-mermaid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mermaid Test - Anonymous GitHub</title>
<!-- Include CSS from the built bundle -->
<link rel="stylesheet" href="public/css/bootstrap.min.css">
<link rel="stylesheet" href="public/css/github-markdown.min.css">
<link rel="stylesheet" href="public/css/mermaid.css">
<link rel="stylesheet" href="public/css/style.css">
<style>
body { padding: 20px; }
.test-section {
margin: 30px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.markdown-body { max-width: 100%; }
.status {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.success { background-color: #d4edda; color: #155724; }
.error { background-color: #f8d7da; color: #721c24; }
</style>
</head>
<body>
<div class="container">
<h1>Mermaid Support Test</h1>

<div class="status" id="status">
<strong>Status:</strong> <span id="statusText">Loading...</span>
</div>

<!-- Test markdown content -->
<div class="test-section">
<h2>Test Input (Markdown)</h2>
<pre id="markdownInput"># Test Document

## Simple Flowchart
```mermaid
graph TD
A[Start] --> B{Test Working?}
B -->|Yes| C[✅ Success!]
B -->|No| D[❌ Failed]
D --> E[Check Console]
C --> F[Mermaid Renders!]
```

## Sequence Diagram
```mermaid
sequenceDiagram
participant User
participant App
participant Mermaid

User->>App: Load page
App->>Mermaid: Render diagram
Mermaid-->>App: SVG output
App-->>User: Display diagram
```</pre>
</div>

<div class="test-section">
<h2>Rendered Output</h2>
<div class="markdown-body" id="renderedOutput">
<!-- Rendered content will appear here -->
</div>
</div>

<div class="test-section">
<h2>Console Output</h2>
<pre id="consoleOutput" style="background: #f8f9fa; padding: 10px; max-height: 200px; overflow-y: auto;"></pre>
</div>
</div>

<!-- Include JavaScript libraries in correct order -->
<script src="public/script/external/jquery-3.4.1.min.js"></script>
<script src="public/script/external/github-emojis.js"></script>
<script src="public/script/external/marked-emoji.js"></script>
<script src="public/script/external/marked.min.js"></script>
<script src="public/script/external/katex.min.js"></script>
<script src="public/script/external/marked-katex-extension.umd.min.js"></script>
<script src="public/script/external/mermaid.min.js"></script>
<script src="public/script/external/marked-mermaid.js"></script>
<script src="public/script/utils.js"></script>

<script>
// Capture console logs
const originalLog = console.log;
const originalError = console.error;
const consoleOutput = document.getElementById('consoleOutput');

function addToConsole(type, message) {
consoleOutput.textContent += `[${type.toUpperCase()}] ${message}\n`;
consoleOutput.scrollTop = consoleOutput.scrollHeight;
}

console.log = function(...args) {
originalLog.apply(console, args);
addToConsole('log', args.join(' '));
};

console.error = function(...args) {
originalError.apply(console, args);
addToConsole('error', args.join(' '));
};

// Test the Mermaid integration
document.addEventListener('DOMContentLoaded', function() {
const statusText = document.getElementById('statusText');
const status = document.getElementById('status');
const markdownInput = document.getElementById('markdownInput').textContent;
const renderedOutput = document.getElementById('renderedOutput');

try {
console.log('Starting Mermaid test...');

// Check if required functions are available
if (typeof marked === 'undefined') {
throw new Error('marked.js not loaded');
}
if (typeof mermaid === 'undefined') {
throw new Error('mermaid.js not loaded');
}
if (typeof markedMermaid === 'undefined') {
throw new Error('marked-mermaid.js not loaded');
}
if (typeof renderMD === 'undefined') {
throw new Error('renderMD function not loaded');
}

console.log('All required libraries loaded');

// Test the rendering
const rendered = renderMD(markdownInput);
renderedOutput.innerHTML = rendered;

console.log('Markdown processed, checking for Mermaid elements...');

// Check if mermaid elements were created
const mermaidElements = renderedOutput.querySelectorAll('.mermaid');
console.log(`Found ${mermaidElements.length} mermaid elements`);

if (mermaidElements.length > 0) {
statusText.textContent = `SUCCESS! Found ${mermaidElements.length} Mermaid diagram(s)`;
status.className = 'status success';

// Wait a bit for diagrams to render
setTimeout(() => {
mermaidElements.forEach((el, index) => {
const svg = el.querySelector('svg');
if (svg) {
console.log(`Diagram ${index + 1}: Successfully rendered as SVG`);
} else {
console.log(`Diagram ${index + 1}: Still rendering or failed`);
}
});
}, 2000);
} else {
statusText.textContent = '❌ FAILED: No Mermaid elements found in rendered output';
status.className = 'status error';
}

} catch (error) {
console.error('Test failed:', error.message);
statusText.textContent = `❌ ERROR: ${error.message}`;
status.className = 'status error';
}
});
</script>
</body>
</html>
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载