requirements:
- VSCode
- markdown-preview-enhanced extension
Intro
I recently stumbled across this site from microsoft and I was thinking: "Hey, why don't we have this in regular markdown!? This looks usefull!"
I am a markdown enthusiast and love to take notes quickly in a informative way. I personally use the VSCode Extension: markdown-preview-enhanced.
I love it because you can:
- define your own styling within a
.less
file - have multiple options to convert markdown into HTML/PDF/PNG/JPEG
- embed images easily
To enhance my experience I was thinking to embed those badges myself but it should be extensible and easy to use in markdown itself. The question just was how?
Luckily the extension provides a extended parser which includes
handlers to decide what should happen before the file gets converted into markdown and what should happen with the data after it got converted into HTML.
You can adjust them in: ~/.mume/parser.js
or simply press CTRL+SHIFT+P
and type "extended parser".
Be aware that this was used primarily to parse as HTML, basically the preview window. It may cause issues in PDF.
Images
Markdown Version compiled as citations:
HTML Version
Usage and adjustable variables
Before you copy the code over, I want to let you know how to configure everything.
I implemented two options: either use plain markdown and display a kind of badges or use the advanced styled HTML version.
Just toggle this one:
const useHTMLBadges = true;
Here comes the fun part, in this object you can specify some colors and emojis to use for your badges or even include new ones or alter existing ones.
To find emojis:
- for markdown you can use this github gist
- in windows you can press
WIN + .
to open the emoji keyboard
const notificationTypes = {
NOTE: {
header: 'Note',
md_icon: ':hand:',
html_icon: '✋',
background: '#e2daf1',
color: '#38225d',
},
TIP: {
header: 'Tip',
md_icon: ':bulb:',
html_icon: '💡',
background: '#d2f9d2',
color: '#094409',
},
INFO: {
header: 'Information',
md_icon: ':heavy_exclamation_mark:',
html_icon: '❗',
background: '#e0f2ff',
color: '#002b4d',
},
WARNING: {
header: 'Warning',
md_icon: ':warning:',
html_icon: '⚠',
background: '#fff1cc',
color: '#664b00',
},
CAUTION: {
header: 'Be careful!',
md_icon: ':no_entry_sign:',
html_icon: '🚫',
background: '#ffdacc',
color: '#651b01',
},
};
Usage
In markdown you use your specified things in the object above.
Be sure to make at least 1 empty line between two badges.
[!NOTE] This is a note!
[!WARNING] This is a warning!
Code
Open the file: ~/.mume/parser.js
and copy the code below in there or even adjust it as you like.
// ? https://shd101wyy.github.io/markdown-preview-enhanced/#/extend-parser
const useHTMLBadges = true;
const notificationTypes = {
NOTE: {
header: 'Note',
md_icon: ':hand:',
html_icon: '✋',
background: '#e2daf1',
color: '#38225d',
},
TIP: {
header: 'Tip',
md_icon: ':bulb:',
html_icon: '💡',
background: '#d2f9d2',
color: '#094409',
},
INFO: {
header: 'Information',
md_icon: ':heavy_exclamation_mark:',
html_icon: '❗',
background: '#e0f2ff',
color: '#002b4d',
},
WARNING: {
header: 'Warning',
md_icon: ':warning:',
html_icon: '⚠',
background: '#fff1cc',
color: '#664b00',
},
CAUTION: {
header: 'Caution',
md_icon: ':no_entry_sign:',
html_icon: '🚫',
background: '#ffdacc',
color: '#651b01',
},
};
// HELPERS
const errorParser = (err) => `<pre>${err.stack}</pre>`;
const markdownParse = (markdown) => {
// [!NOTE] Example Text
let notificationRegex = null;
for ([notificationType, notificationInfo] of Object.entries(
notificationTypes,
)) {
// multi line comments
notificationRegex = new RegExp(
String.raw`\[\!${notificationType}\](.+\r?\n?)+(?=(\r?\n)?)`,
'gm',
);
markdown = markdown.replace(notificationRegex, (message) => {
return `> ${notificationInfo.md_icon} **${notificationInfo.header}**
${message.substr(message.indexOf(' ') + 1)}`;
});
}
return markdown;
};
const htmlParse = (html) => {
const findCitations = (html) =>
html.match(/<blockquote>[\S\s]*?<\/blockquote>/gi);
const findBadges = (citationArray) => {
let realBadges = [];
for (index in citationArray) {
for ([_, info] of Object.entries(notificationTypes)) {
// minified version spits out <br> instead of <br />
if (
citationArray[index].match(
`<strong>${info.header}<\/strong><br>`,
'gm',
)
) {
realBadges[index] = citationArray[index];
}
}
}
return realBadges;
};
let badges = findCitations(html);
badges = findBadges(badges);
const getBadgeInfos = (badgeElement) => {
let findTitle = '';
for ([_, info] of Object.entries(notificationTypes)) {
// returns a array of matches
// minified version spits out <br> instead of <br />
findTitle = badgeElement.match(
`.*<strong>${info.header}<\/strong><br>`,
'gm',
);
if (findTitle != null) {
if (findTitle.length != 0) return info;
}
}
return {};
};
const getBody = (badgeElement) => {
// minified version spits out <br> instead of <br />
const findBody = badgeElement.match(`(?<=<br>)(.|\r?\n)*?(?=<\/p>)`);
if (findBody != null) {
if (findBody.length != 0) return findBody[0].trim();
}
return '';
};
let infos = {};
let body = '';
for (index in badges) {
html = html.replace(badges[index], (foundElement) => {
infos = getBadgeInfos(foundElement);
body = getBody(foundElement);
if (infos == null)
return `<div style="color:red">parsing error, see: <pre>~/.mume/parser.js</pre></div>`;
const styling = `style="padding: 0.8rem 1rem; border-radius: 6px; margin: 1rem 0; background-color:${info.background}"`;
return `<div ${styling}><p>${info.html_icon} <strong style="color: ${info.color}">${info.header}</strong></p><p style="margin: 0; text-align: left; line-height:1.3;">${body}</p></div>`;
});
}
return html;
};
module.exports = {
// do something with the markdown before it gets parsed to HTML
onWillParseMarkdown: function (markdown) {
return new Promise((resolve, reject) => {
try {
markdown = markdownParse(markdown);
} catch (error) {
markdown = errorParser(error);
}
return resolve(markdown);
});
},
// do something with the parsed HTML string
onDidParseMarkdown: function (html) {
return new Promise((resolve, reject) => {
try {
html = useHTMLBadges ? htmlParse(html) : html;
} catch (error) {
html = errorParser(error);
}
return resolve(html);
});
},
};
Last words
If you want to revert the changes or something broke, just remove everything except the handlers:
module.exports = {
// do something with the markdown before it gets parsed to HTML
onWillParseMarkdown: function (markdown) {
return new Promise((resolve, reject) => {
try {
markdown = markdownParse(markdown);
} catch (error) {
markdown = errorParser(error);
}
return resolve(markdown);
});
},
// do something with the parsed HTML string
onDidParseMarkdown: function (html) {
return new Promise((resolve, reject) => {
try {
html = useHTMLBadges ? htmlParse(html) : html;
} catch (error) {
html = errorParser(error);
}
return resolve(html);
});
},
onWillTransformMarkdown: function (markdown) {
return new Promise((resolve, reject) => {
return resolve(markdown);
});
},
onDidTransformMarkdown: function (markdown) {
return new Promise((resolve, reject) => {
return resolve(markdown);
});
},
};
My code won't be perfect but I am happy with my result.
Any improvements are welcome! Happy coding. :)
Top comments (3)
Thanks, for the tips!
To sync my note to the cloud and my phone, I have also included joplin-vscode extension
rxliuli.com/joplin-vscode-plugin/#/
Then to make it nicer with my dark theme, I swapped the background with the text color.
Great thing about MPE is that you can enhance it.
I have also tried added music sheet parser, and some custom markdown syntaxes.
I was searching for articles on how to improve an aesthetic problem that I was having in VScode when I came across this post and this thread I'm using markdown-preview-enhanced and I find your post to be extremely helpful! - searching for something lead me to another! :D