Add KaTeX SSR support

Hi.
I try to add the KaTeX server side rendering feature to Zola. if Zola has this, it will not be necessary to render KaTeX formulas on browser. KaTeX formulas will be extracted into HTML on server side. Such that web pages will be rendered much faster !

options:

  • in site config

    [katex]
    enable = true
    restrict = true
    css_url = "https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css"
    css_integrity = "sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ"
    
    [[katex.delimiters]] 
    left = "$$"
    right = "$$"
    display = true
    
    [[katex.delimiters]] 
    left = "$"
    right = "$"
    display = false
    

    if restrict is true, KaTeX syntax error will cause zola panic. else it will render a warning text in outputted HTML.
    if enable is false. Zola wonā€™t render any formula.

  • in frontmatter

    +++
    katex = true
    +++
    

What do you guys think about?

1 Like

I would like support for katex, but I would probably prefer it to be at the templating layer. Like instead of piping markdown text to be rendered as html, piping latex to be rendered as html.

Itā€™s not difficult to integrate katex into templating layer. but itā€™s difficult to avoid the ugly Tera syntax.

Default:

$$F=ma$$

Tera Function

{{ tex(code = "F = ma", display = true) }}

if you have a good way to optimize this, Please tell me. I will be glad to use this plan.

Hi Narisuzu, I think I may have made the mistake of not thinking hard enough before answering your question, so apologies for that. Iā€™ll do more to brainstorm here with you.

First, how would KaTeX normally be utilized? In the browser, it would look something like this:

katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, {
    throwOnError: false
});

There are two things we need to know: 1. ) what is the KaTeX expression we are looking to render and 2.) where do we want it to be rendered, in the context of the rest of the page?

However, the whole problem weā€™re looking to solve is provide some SSR solution to utilizing KaTeX, within the framework and design of Zola. Bearing this in mind, I see maybe a few places we could do this.

One, is within the context of a markdown file. In this context, point #2 (I.e. where the element should be rendered) is determined by the location in the markdown file. I could imagine there being two places this could done: a.) within a code block, like:

```KaTeX
c = \pm\sqrt{a^2 + b^2}
```

and b.) the other way I imagine it could be used within markdown would be via a shortcode.

I donā€™t think thereā€™s any other way to use Tera within markdown other than through shortcode, so it seems like code blocks and shortcodes is all that would be available to use within a markdown context. Although correct me if Iā€™m wrong there.

Besides markdown, I also think weā€™d probably also want the ability to inject rendered KaTeX within the context of the actual HTML code, although I think that would be less common. So in Zola thatā€™s basically sections, pages, and macros I believe.

After thinking about it a bit, I feel like all the templating stuff (which so far is within a shortcode, sections/pages, and macros) could probably be best done at the templating layer. I donā€™t think this

{{ tex(code = "F = ma", display = true) }}

is honestly so bad. Maybe the one thing Iā€™d add is can I also read KaTeX from a file and pipe it to the tex function?

That leaves supporting it in markdown via the codeblocks. To be honest, this is where I would use KaTeX 95% of the time anywayā€¦ Maybe there could be a branch in the syntax highlighting logic (within Zola) that looks for if the codeblock is marked as katex. If it is, then it passes it to the KaTeX rendered. Otherwise, then it goes through the normal code highlighting flow. Also, youā€™d probably want the ability to show KaTeX unrendered within code blocks as well, so maybe for that you could saw katex-raw or something.

Iā€™m not sure if thatā€™s helpful at all. This is just me thinking aloud and where Iā€™m coming from as a user and how I would probably use KaTeX in Zola. Another thing as a user that I would care a lot about is KaTeX support not slowing down how long it takes to compile my site. If using KaTeX in one place on my website slowed down the build by 250ms or something, Iā€™d be pretty annoyed. So, I donā€™t know if it would work to call out to a nodejs process for each individual case that you have to generate HTML. Maybe thereā€™s a library in rust that can be embedded within the Zola code to do this fast, but you probably know a lot more about that than I do.

Any way, apologies for the wall of text. Iā€™m happy to discuss more offline too. You can find me on GH as asimpletune, or on my website Contact - Spenc.es and Iā€™m happy to collaborate where I can. Best of luck!

Thanks for your ideas!

  1. We can use this to get a simpler syntax. it will be like:

    {{ "F=ma" | tex }}
    

    or

    {% filter tex %}
     F = ma
    {% endfilter %}
    
  2. I think it is possible to read KaTeX code from a file. it probably will be like

    {{"path/to/file" | tex_from_path }}
    
  3. code block
    To implement this, I think itā€™s necessary to modify Here and line 243, which will allow zola checking whether the code block is a KaTeX formula. and render it accordingly.

  4. performance
    the KaTeX lib I used is called katex-rs(Sorry for that I cannot put more URLs, but you can search this on Github). This lib chose some Rust JS engines as its backend, instead of a Node.js instance. Indeed, it will be better if we have an implementation with pure rust. but I cannot find a lib to do this.

I will go on attempting to implement these (ā€˜tera filterā€™ and ā€˜code blockā€™). I will share with you once I have any improvement. I really appreciate your help so much.

Cool! Yeah sounds interesting, Iā€™d be happy to help whenever you have something to share. Iā€™m asimpletune on GitHub.

for display mode There are two way to implement.

  1. {{"F=ma"| tex_display }}: make a new function.
  2. {{"F=ma" | tex(display = true) }}: use same function, but pass a parameter.

I donā€™t know which is better. the first one is obviously simpler. but the second one is more configurable.

There was a PR for that: Katex by technicalguy Ā· Pull Request #1073 Ā· getzola/zola Ā· GitHub but it didnā€™t work on Windows at the time. I would take a PR to add support to Zola

I like {{"F=ma" | tex(display = true) }} best of all. Maybe thereā€™s also a way to have the display parameter take true as a default as well?

I would also be very interested in this. Having MathJax 3 as a JS dependency adds >160KB to my website footprint, which is almost double. But not having $$ equation goes here $$ would be a hard sell for my scientific colleagues, and this is the level of convenience that MathJax currently supports.

The alternative I considered was to produce SVGs like so: https://latextosvg.com/

Having them compile to anything that can be rendered with JS disabled is preferrable for the purpose of having the website always work (offline, archived, transfered, independent of CDN and JS engines).

I wonā€™t suggest any concrete ways to make $$ equation $$s work, since I donā€™t know enough about Zolaā€™s principles. But Iā€™ll probably stick with MathJax until I find an easy way to make $$ equations $$ compile to either SVG or KaTeX. :slight_smile:

Itā€™s basically done but the macros rendering within a page. It is impossible for Tera funtions/filters to get the macros definition during rendering a page(except some hacky tricks, like, clone and expand the current tera with registering a specific katex filter which the macros defined in the frontmatter are passed to for each page. or set a global hashmap to store the macro definitions in each page)

However, I noticed that @keats has a plan to let filters be able to access context in next version of tera. https://github.com/Keats/tera/issues/543. So I decide to wait for this update of tera. So that, We are able to define katex macros in the frontmatters and put them in tera context. and register a katex filter just like other filters inside the components/templates sub-crate, which is a better way to handle the macros in page.

Thatā€™s potentially a long time! I have a new lexer/parser written but Iā€™m rewriting them to remove a dependency. Going very very slowly though as itā€™s not a priority currently, i spend maybe 1h a week on it if iā€™m spending time on it.

Hi! Let me take a closer look at this within the next few days. Iā€™m busy traveling right now, but I can probably spend a little time on this next week.

Maybe in the meanwhile you can clarify a few things? Iā€™m sure itā€™ll be crystal clear when I actually look at the code, but it doesnā€™t hurt to ask before then.

Itā€™s basically done but the macros rendering within a page

Iā€™m not sure I understand this macro part. Is the context Iā€™m missing that: youā€™ve implemented this new katex feature as a tera function, for example {{"F=ma" | tex(display = true) }} (to use the example from before), but when you want to use this function from within a macro it doesnā€™t have some context that it needs? (and this context is usually available within a Zola page/section?)

Did I understand that correctly?

It is impossible for Tera funtions/filters to get the macros definition during rendering a page
[ā€¦]
However, I noticed that @keats has a plan to let filters be able to access context in next version of tera.

Iā€™m not sure I really follow what context is needed where. Like, thereā€™s a page being rendered, and a tera filter being used, but it needs the ā€œmacroā€ definition? Thatā€™s the last part Iā€™m not sure I quite follow.

Ok but with this said, Iā€™m also assuming that whatever dependency that is needed (I.e. macro definition above), can be gotten somewhere (I.e. in a page maybe? Not just a filter). So, technically, could the result of doing that just be passed along to the filter as an argument for now?

Maybe Iā€™m way off base here, Iā€™m sort of piecing this together from reading your comment and havenā€™t looked at the code haha. So Iā€™m sure itā€™ll be crystal clear when I take a closer look, but if you have a moment to respond to this comment before then, then great and thanks!

O and also I didnā€™t see a PR on the Zola repo so maybe you could also tell me where the code lives too.

Also, hereā€™s an idea that just came to meā€¦ but if there are parts of this working, it wouldnā€™t be the end of the world to break this up into separate PRs and at least get something thatā€™s working merged upstream. Like, for example, if the syntax highlighting part is done, that covers a pretty big use case already.

the macros I mentioned above means the KaTeX Macros. Info. I think users should be able to set different macros in different page. So the macros can be defined in 2 ways: Global macros which is defined in site config and page macros in frontmatters.

and here it is