Skip to content

Instantly share code, notes, and snippets.

@kiennq
Last active June 18, 2024 22:26
Show Gist options
  • Save kiennq/890bebf82a801867ba2366b03ea47d35 to your computer and use it in GitHub Desktop.
Save kiennq/890bebf82a801867ba2366b03ea47d35 to your computer and use it in GitHub Desktop.
Enable `Mermaid diagrams` on github wiki and markdown files
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.pnp.*
// ==UserScript==
// @name Mermaid Diagrams
// @namespace https://gist.github.com/kiennq
// @version 1.1.3
// @author Kien Nguyen <kien.n.quang at gmail dot com>
// @description Enable `Mermaid diagrams` on github wiki and markdown files
// @homepage https://github.com/Redisrupt/mermaid-diagrams
// @downloadURL https://gist.github.com/kiennq/890bebf82a801867ba2366b03ea47d35/raw/mermaid-diagrams.user.js
// @updateURL https://gist.github.com/kiennq/890bebf82a801867ba2366b03ea47d35/raw/mermaid-diagrams.user.js
// @match https://*.visualstudio.com/*
// @match https://dev.azure.com/*
// @match https://github.com/*
// @match https://gist.github.com/*
// @match https://bitbucket.org/*
// @match file:///*
// @run-at document-idle
// @grant none
// ==/UserScript==
(async () => {
'use strict';
console.log('Load');
const $ = (selector, ctx = document) => [].slice.call(ctx.querySelectorAll(selector));
const { default: mermaid } = await import('https://cdn.jsdelivr.net/npm/[email protected]/+esm');
mermaid.initialize({
startOnLoad: false,
theme: 'default',
flowchart: {
curve: 'linear',
},
});
function addGlobalStyle(css) {
let head = $('head')[0];
if (!head) { return; }
let style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
}
addGlobalStyle(`@keyframes mermaidDiagramCodeInserted {
from { opacity: 0.99; }
to { opacity: 1; }
}`);
addGlobalStyle(`.language-mermaid, [lang="mermaid"], .hljs {
animation-duration: 0.001s;
animation-name: mermaidDiagramCodeInserted;
}`);
async function setupChart(elem, code) {
var sourceName = elem.id;
if (elem.id == "") {
const postfix = Math.random().toString(36).substr(2, 9);
sourceName = 'id-' + postfix;
elem.id = sourceName;
}
var mermaidName = 'mermaid-' + sourceName;
let existingDiagrams = $(`#${mermaidName}`);
let existingDiagram = null;
if (existingDiagrams.length > 0) {
existingDiagram = existingDiagrams[0];
existingDiagram.innerHTML = code;
} else {
// Create the element that will house the rendered diagram.
elem.insertAdjacentHTML('afterend', `<div id="${mermaidName}" />`);
existingDiagram = $(`#${mermaidName}`)[0];
// Create an observer to track changes to the diagram code.
const observer = new MutationObserver(() => { processElement(elem) });
observer.observe(elem, { characterData: true });
}
try {
// Generate or regenerate diagram if it is existing.
const {svg} = await mermaid.render('svg-' + mermaidName, code);
elem.style.display = 'none';
existingDiagram.innerHTML = svg;
}
catch (error) {
console.log("Err: " + error);
existingDiagram.style.display = 'none';
let svg = $(`#${'svg-' + mermaidName}`)[0];
svg.style.display = 'none';
}
};
function processElement(elem) {
const codeElem = $('code', elem)[0];
if (codeElem !== undefined) {
const code = codeElem.textContent;
setupChart(elem, code);
} else {
const code = elem.textContent;
setupChart(elem, code);
}
};
function onElementInsert(event) {
// We are only interested in the diagrams that trigger the css animation
// called "mermaidDiagramCodeInserted". This is determined by the file
// "on_change_animations.css".
if (event.animationName !== "mermaidDiagramCodeInserted") {
return
}
processElement(event.target);
}
// These will be run after DOMContentLoaded
// Github
$('[lang="mermaid"]').forEach(processElement);
$('[lang="mermaid-x"]').forEach(processElement);
$('.language-mermaid').forEach(processElement);
// This catches diagrams that are added to the page after it is loaded.
// This might include comments from other users.
document.addEventListener("animationstart", onElementInsert, false);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment