KaTeX with VSCode, Jekyll, and WordPress Markdown

I use KaTeX in VSCode extension Markdown All in One for notes and homework. It's by far the easiest and most flexible way.

Markdown is such a markup language (see the paradox? lol) without a widely-accepted, strong official organization regularizing its syntax and grammar. Typical standards includes:

Implementation of Javascript libraries into Markdown, such as KaTeX, is done independently by Markdown processors, thus creating even more variants of syntax and grammar.

In an attempt to use the same syntax and grammar throughout my Markdown documents, i.e KaTeX with VSCode, Jekyll, and WordPress Markdown, and considering the possible transplantation to LaTeX, I adopt the following configuration for most consistent experience.

VSCode

Markdown All in One, with default settings such that the following is adopted

inline equation $c$ and block equation is coded by

$$
E = mc^2
$$

rendered as:

inline equation c and block equation is coded by

E = mc^2

other syntax like \(\) and \[\] are also supported.

Jekyll

Two methods, server-side and client-side, are experimented. Client-side method is preferred due to flexibility on custom delimiter syntax.

Server-side Method

Server-side gem

# KaTeX
gem 'katex', '~> 0.6.0'
gem "kramdown-math-katex"

config.yml

# Build settings
markdown: kramdown
kramdown:
  math_engine: katex
  math_engine_opts: {}

To support inline syntax $, do the following change in ~/gems/gems/kramdown-2.3.0/lib/kramdown/parser/kramdown/math.rb

      #INLINE_MATH_START = /\$\$(.*?)\$\$/m
      INLINE_MATH_START = /\$(.*?[\S])\$/m

Off topic, the footnote regex need to modify as well, in ~/gems/gems/kramdown-2.3.0/lib/kramdown/parser/kramdown/footnote.rb

ALD_FN_NAME = /[^\[]*/
FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_FN_NAME})\]:\s*?(.*?\n#{CODEBLOCK_MATCH})/
FOOTNOTE_MARKER_START = /\[\^(#{ALD_FN_NAME})\]/

Client-side Method

No gem required.

config.yml

# Build settings
markdown: kramdown
kramdown:
  math_engine: null

such that default math_engine: mathjax is not enabled.

HTML template header

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa" crossorigin="anonymous"></script>

HTML template footer

<script>
// Load KaTeX
document.addEventListener("DOMContentLoaded", function () {
  renderMathInElement(document.body, {
    delimiters: [
      { left: "$$", right: "$$", display: true },
      { left: "$", right: "$", display: false },
      { left: "\(", right: "\)", display: false },
      { left: "\[", right: "\]", display: true },
    ],
  });
});
</script>

thus defined all 4 delimiters. Note that this method is potentially dangerous since delimiters are used instead of regex.

WordPress

I use Githuber MD as my preferred github editor. I've tried WP-Editor.md but that one gots too many bugs even after years of development.

A quick and dirty hack to get my delimiters work:

replacing original inline style with display style,

wp-githuber-md/src/Modules/KaTeX.php

    public static function katex_inline_markup( $content ) {
        //replace original incline style with display style
        //$regex = '%<code>\$\$((?:[^$]+ |(?<=(?<!\\\\)\\\\)\$ )+)(?<!\\\\)\$\$<\/code>%ix';
        $regex = '%(\$\$)((?:[^$]+ |(?<=(?<!\\\\)\\\\)\$ )+)(?<!\\\\)(\$\$)%ix';
        $content = preg_replace_callback( $regex, function() {
            $matches = func_get_arg(0);

            if ( ! empty( $matches[1] ) ) {
                $katex = $matches[1];
                $katex = str_replace( array( '<', '>', '"', ''', '&', '&', "\n", "\r" ), array( '<', '>', '"', "'", '&', '&', ' ', ' ' ), $katex );
                //solve escape problem, temporarily 
                $katex = str_replace( '\\', '\\\\', $katex );
                $katex = str_replace( '_', '\_', $katex );
                //return '<code class="katex-inline">' . trim( $katex ) . '</code>';
                return $katex;
            }
        }, $content );

        return $content;
    }

Reorder the rendering steps to have KaTeX rendered before Markdown.

wp-githuber-md/src/Controllers/Markdown.php

// If we're not using the code shortcode, prevent over-encoding.
if ( $args['decode_code_blocks'] ) {
    $text = $this->restore_code_blocks( $text );
}

// Reordered Render KaTeX inline markup.
if ( $this->is_support_katex ) {
    $text = ModuleKaTeX::katex_inline_markup( $text );
}

// Transform it!
$text = $this->get_parser()->transform( $text );

// Fetch remote images.
if ( $this->is_convert_remote_image() ) {
    $text = $this->convert_remote_image( $text );
}

// Render Github Flavored Markdown task lists if this module is enabled.
if ( $this->is_support_task_list ) {
    $text = ModuleTaskList::parse_gfm_task_list( $text );
}

// Render KaTeX inline markup.
// if ( $this->is_support_katex ) {
//    $text = ModuleKaTeX::katex_inline_markup( $text );
//}

// Render MathJax inline markup.
if ( $this->is_support_mathjax ) {
    $text = ModuleMathJax::mathjax_inline_markup( $text );
}

// Markdown inserts extra spaces to make itself work. Buh-bye.
$text = rtrim( $text );

then add same code to header and footer as Jekyll client method

HTML template header

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-g7c+Jr9ZivxKLnZTDUhnkOnsh30B4H0rpLUpJ4jAIKs4fnJI+sEnkvrMWph2EDg4" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-mll67QQFJfxn0IYznZYonOWZ644AWYC+Pt2cHqMaRhXVrursRwvLnLaebdGIlYNa" crossorigin="anonymous"></script>

HTML template footer

<script>
// Load KaTeX
document.addEventListener("DOMContentLoaded", function () {
  renderMathInElement(document.body, {
    delimiters: [
      { left: "$$", right: "$$", display: true },
      { left: "$", right: "$", display: false },
      { left: "\(", right: "\)", display: false },
      { left: "\[", right: "\]", display: true },
    ],
  });
});
</script>

and we are all set.

Reference

kramdown/math-katex: kramdown-math-katex uses KaTeX to convert math elements to HTML on the server side

Auto-render Extension · KaTeX