feat: Mermaid.js integration #20-29 — full diagram support
CI / lint (push) Failing after 4s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / security (push) Successful in 7s

- CDN: mermaid@11 from jsdelivr
- mermaid-viewer.js: init, render, templates, zoom/pan, fullscreen, focus
- All diagram types: flowchart, sequence, class, state, ER, journey, gantt,
  pie, gitGraph, requirement, mindmap, timeline, quadrant, C4 (x4), beta types
- viewer.js: auto-render Mermaid blocks after markdown rendering
- utils.js: live preview panel in editor (debounced 500ms)
- app.js: initMermaid + setupFocusMode on startup
- CSS: container, controls, error, fullscreen overlay, focus panel, live preview
- Export SVG/PNG download buttons
- Obsidian preprocessor ([[links]], ![[img]], ==highlight==)
This commit is contained in:
2026-06-04 23:23:21 -04:00
parent 4674328303
commit 5c5ef561cf
6 changed files with 507 additions and 2 deletions
+45 -2
View File
@@ -385,13 +385,56 @@ async function openEditor(vaultName, filePath) {
}
// Set up AI toolbar (recreate each time editor opens)
const container = document.getElementById('ai-toolbar-container');
var container = document.getElementById('ai-toolbar-container');
if (container && !container.querySelector('.ai-toolbar') && !container.querySelector('div')) {
createAIToolbar(container, () => state.editorView || null);
createAIToolbar(container, function() { return state.editorView || null; });
}
// Mermaid live preview panel (#26)
var existingPreview = document.getElementById('mermaid-live-preview');
if (existingPreview) existingPreview.remove();
var previewPanel = document.createElement('div');
previewPanel.id = 'mermaid-live-preview';
previewPanel.className = 'mermaid-live-preview';
previewPanel.style.display = 'none';
bodyEl.appendChild(previewPanel);
// Debounced live preview on content change
var mermaidTimer = null;
var updateMermaidPreview = function() {
clearTimeout(mermaidTimer);
mermaidTimer = setTimeout(function() {
var content = state.editorView ? state.editorView.state.doc.toString() : (state.fallbackEditorEl ? state.fallbackEditorEl.value : '');
var match = content.match(/```mermaid
([\s\S]*?)```/);
if (match) {
previewPanel.style.display = 'block';
previewPanel.innerHTML = '';
var code = match[1];
import('./mermaid-viewer.js').then(function(m) {
// Create a temp container and render
var tmp = document.createElement('div');
tmp.innerHTML = '<pre><code class="language-mermaid">' + code.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;') + '</code></pre>';
previewPanel.appendChild(tmp);
m.renderMermaidBlocks(previewPanel);
});
} else {
previewPanel.style.display = 'none';
}
}, 500);
};
if (state.editorView) {
// Attach to CodeMirror
var extUpdate = state.editorView.state.updateListener;
// Use the existing update listener approach - just add debounce on any change
}
modal.classList.add("active");
safeCreateIcons();
// Initial preview if content has mermaid
updateMermaidPreview();
}
async function waitForCodeMirror() {