I write all my content in Markdown, to be exact in Obsidian. Obsidian is great because it provides some really cool Markdown capabilities that extend the basic Markdown spec. If you want to publish your notes, Obsidian offers a subscription plan to upload an entire vault to a website, called Obsidian Publish.

But i’m not here to pay! And also, I don’t want to be limited by the Obsidian publish theme, where all websites built with it look more or less the same. I want to customize my Markdown styling to the last percent! Finally, my website uses Laravel and has a lot more content than just the notes.

While Laravel comes bundled with a great Markdown parser called CommonMark, it does not satisfy all my needs. CommonMark flawlessly renders basic Markdown and parts of the GitHub flavoured Markdown spec as well, but lacks support for some Obsidian features, which I heavily rely on. And so i decided to implement the feaures as extensions for the CommonMark parser.

Code Highlighting

Obsidian uses a code highlighting library called Prism.js. Initially i wanted to use Prism.js but i ran into bugs so i searched for an alternative. I came across Torchlight, which has a different approach to code highlighting. Instead of relying on the client, Torchlight highlights the syntax on the server with an API call. I found this approach to be really intuitive, as sending a JavaScript highlighting library wold result in a larger JavaScript bundle and longer processing times on the client browser. Unfortunately, despite following this YouTube tutorial by the creator of Torchlight himself, Aaron Francis, i could not get Torchlight to run. But the idea of server-side code highlighting stuck in my head.

Solution: HighlightJs for PHP

The most popular highlighting engine is highlight.js. So I asked myself if there may be a PHP version of this. And luckily, there is. highlight.php does exactly what i wanted. It takes some code and an optional coding language, and returns the code split into spans with different classes. These can be easily styled with basic CSS. Converting this to a CommonMark PHP extension was easy.

Math Rendering

Math rendering was essential because I wrote all my lecture notes this way. I decided to use MathJax, as it is the same LaTex renderer as Obsidian uses.

Initially, I had problems getting MathJax to load in my Laravel application. I didn’t find any package which i could install and then simply add the imports in my JavaScript and CSS.

Instead, I used a “hacky” way: I used the Vite Static Copy Plugin to copy the dist files from the MathJax node_modules package into the public directory and ran it. Then i hardcoded the <script src>tag into my layout.

I encountered an additional problem: LaTex source code could still be parsed by CommonMark and add some unwanted HTML tags, which would mess up LaTex rendering. In this example:

$\mathrm{C2FA}_{16}$ (Hex) $01001011_{2}$ (Bin)

CommonMark would introduce an <em> tag here because of the underscores:

$\mathrm{C2FA}<em>{16}$ (Hex) $01001011</em>{2}$ (Bin)

MathJax would then fail to recognize and correctly render the LaTex code. So i needed to escape code enclosed by dollar signs. For this, i implemented two extensions, one for inline math and one for multi-line math blocks.

Callouts

Callouts extend the GitHub Alerts syntax by letting the author specify the type of alert as well as the title of the alert box. This example:

> [!note] Look at me!
> 
> This is some cool stuff.

Will render like this:

Look at me!

This is some cool stuff.

Wikilinks are a simpler form of Markdown links. Any text enclosed with two square brackets in Obsidian becomes a Wikilink. This link refers to the nearest relative file found with the link’s name. Additionally, if you add a vertical dash | followed by some text, this will become the link text. Example:

  • [[Note]] will link to a File called Note.md
  • [[image.jpg|Look at me!]] will link to image.jpg with some display text

As Wikilinks only contain the filename and not the path where they are actually located, the extension needs a way to know how to convert the filename to the actual path. Initially, I just provided an asset_path which was simply prefixed to the filename.

But I anticipated a more complex scenario in which these files may not be in a general location that can easily be prefixed, but possibly in multiple locations. That’s why I refactored the extension to require a resolve_wikilink function which takes the Wikilink filename and returns the fully qualified link. This function needs to be implemented by the respecting application.

As for embedded Wikilinks, i added a customizable interface where different types of embed are each processed by their individual renderer. Currently, I have an image renderer and a default fallback which just renders a link to the embedded resource. In the future, i want to add video and audio renderers as well.

Future Ideas

I am looking forward to attempt to get Blade templates to run in my Markdown rendering process, like shown by Aaron francis. These templates could then render e.g. a privacy-friendly YouTube embed, SoundCloud track, Spotify preview or Bandcamp release.

Source Code

I am planning to release all my CommonMark PHP Extensions on GitHub, stay tuned!

Notes

Edit 2025-02-28

I created the GitHub Repository and uploaded my extensions. They are currently directly taken from my website and do not work completely as a standalone library. But i am workin on it! Here’s the link to the repo: https://github.com/semmelsamu/commonmark-extensions