-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(markdown): add support for math rendering with typst and katex #2791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
84de5f5
e380fe1
f661eae
9bfb6f8
3a08fb9
dfc5129
06d7c0c
d9323fa
d77b56c
e804654
ba7ef5f
4349f77
77308e2
965b845
385dbbe
8d8cf3c
348e5e0
7847d86
689d706
18b7333
386f4e9
f1ba873
b1a526e
01bb110
0605915
8f335f1
821e706
fe7d683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,11 @@ | ||
| use std::{path::Path, sync::Arc}; | ||
| use std::{fmt, path::Path, sync::Arc}; | ||
|
|
||
| use libs::syntect::{ | ||
| highlighting::{Theme, ThemeSet}, | ||
| html::css_for_theme_with_class_style, | ||
| parsing::{SyntaxSet, SyntaxSetBuilder}, | ||
| }; | ||
| use serde::{Deserialize, Serialize}; | ||
| use serde::{de, Deserialize, Deserializer, Serialize}; | ||
|
|
||
| use errors::{bail, Result}; | ||
| use utils::types::InsertAnchor; | ||
|
|
@@ -23,6 +23,142 @@ pub struct ThemeCss { | |
| pub filename: String, | ||
| } | ||
|
|
||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] | ||
| #[serde(rename_all = "lowercase")] | ||
| pub enum MathRenderingEngine { | ||
| #[default] | ||
| None, | ||
| Typst, | ||
| Katex, | ||
| } | ||
|
|
||
| #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, Hash)] | ||
| #[serde(rename_all = "lowercase")] | ||
| pub enum ImageFormat { | ||
| #[default] | ||
| Svg, | ||
| Webp, | ||
| } | ||
|
|
||
| struct BoolWithPathVisitor; | ||
|
|
||
| impl<'de> de::Visitor<'de> for BoolWithPathVisitor { | ||
| type Value = BoolWithPath; | ||
|
|
||
| fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { | ||
| formatter.write_str("a boolean or string") | ||
| } | ||
|
|
||
| fn visit_bool<E>(self, value: bool) -> Result<BoolWithPath, E> | ||
| where | ||
| E: de::Error, | ||
| { | ||
| Ok(if value { BoolWithPath::True(None) } else { BoolWithPath::False }) | ||
| } | ||
|
|
||
| fn visit_str<E>(self, value: &str) -> Result<BoolWithPath, E> | ||
| where | ||
| E: de::Error, | ||
| { | ||
| Ok(BoolWithPath::True(Some(value.to_string()))) | ||
| } | ||
|
|
||
| fn visit_string<E>(self, value: String) -> Result<BoolWithPath, E> | ||
| where | ||
| E: de::Error, | ||
| { | ||
| Ok(BoolWithPath::True(Some(value))) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, Debug, PartialEq, Eq, Default, Hash)] | ||
| pub enum BoolWithPath { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's that? Can you add a comment? |
||
| #[default] | ||
| False, | ||
| True(Option<String>), | ||
| } | ||
|
|
||
| impl<'de> Deserialize<'de> for BoolWithPath { | ||
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
| where | ||
| D: Deserializer<'de>, | ||
| { | ||
| deserializer.deserialize_any(BoolWithPathVisitor) | ||
| } | ||
| } | ||
|
|
||
| impl Serialize for BoolWithPath { | ||
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
| where | ||
| S: serde::Serializer, | ||
| { | ||
| match self { | ||
| BoolWithPath::False => serializer.serialize_bool(false), | ||
| BoolWithPath::True(None) => serializer.serialize_bool(true), | ||
| BoolWithPath::True(Some(s)) => serializer.serialize_str(s), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, Debug, Serialize, Default)] | ||
| pub struct MathRenderer { | ||
| pub engine: MathRenderingEngine, | ||
| pub svgo: BoolWithPath, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is that a path to the binary? |
||
| pub css: Option<String>, | ||
| pub addon: Option<String>, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only one addon supported? What are addons? |
||
| pub format: ImageFormat, | ||
| } | ||
|
|
||
| impl<'de> Deserialize<'de> for MathRenderer { | ||
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
| where | ||
| D: Deserializer<'de>, | ||
| { | ||
| // Helper struct that mirrors MathRenderer | ||
| #[derive(Deserialize)] | ||
| struct MathRendererHelper { | ||
| #[serde(default)] | ||
| engine: MathRenderingEngine, | ||
| #[serde(default)] | ||
| svgo: BoolWithPath, | ||
| css: Option<String>, | ||
| addon: Option<String>, | ||
| #[serde(default)] | ||
| format: ImageFormat, | ||
| } | ||
|
|
||
| #[derive(Deserialize)] | ||
| #[serde(untagged)] | ||
| enum MathRendererConfig { | ||
| // String case: math = "engine" | ||
| Engine(MathRenderingEngine), | ||
| // Table case with full configuration | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's just only have the full table so we don't have to support that kind of things |
||
| Full(MathRendererHelper), | ||
| } | ||
|
|
||
| // Parse using the helper type | ||
| let config = MathRendererConfig::deserialize(deserializer)?; | ||
|
|
||
| // Convert to MathRenderer | ||
| match config { | ||
| MathRendererConfig::Engine(engine) => Ok(MathRenderer { | ||
| engine, | ||
| svgo: BoolWithPath::default(), | ||
| css: None, | ||
| addon: None, | ||
| format: ImageFormat::Svg, | ||
| }), | ||
| MathRendererConfig::Full(helper) => Ok(MathRenderer { | ||
| engine: helper.engine, | ||
| svgo: helper.svgo, | ||
| css: helper.css, | ||
| addon: helper.addon, | ||
| format: helper.format, | ||
| }), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| #[serde(default)] | ||
| pub struct Markdown { | ||
|
|
@@ -67,6 +203,12 @@ pub struct Markdown { | |
| pub insert_anchor_links: InsertAnchor, | ||
| /// Whether to enable GitHub-style alerts | ||
| pub github_alerts: bool, | ||
| /// Whether to enable math rendering in markdown files | ||
| pub math: MathRenderer, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about making it an Option? |
||
| /// Whether to cache the rendered math | ||
| pub cache: BoolWithPath, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should that be in MathRenderer?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the idea was to put it here in case we needed cache for other stuff than math rendering in the future, the comment above is erroneous |
||
| /// Whether to enable GitHub-style alerts | ||
| pub github_alerts: bool, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| } | ||
|
|
||
| impl Markdown { | ||
|
|
@@ -243,6 +385,9 @@ impl Default for Markdown { | |
| lazy_async_image: false, | ||
| insert_anchor_links: InsertAnchor::None, | ||
| github_alerts: false, | ||
| math: MathRenderer::default(), | ||
| cache: BoolWithPath::True(None), | ||
| github_alerts: false, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also errors out on this declaration as well |
||
| } | ||
| } | ||
| } | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need both typst and katex?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea behind the generic
MathCompilertrait was to add support for multiple backends, many people still use LaTeX in my opinion.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But for the Zola crowd, do we need multiple renderer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chiming in as a Zola user who's been using cestef's fork: I personally am using the Typst math rendering right now, but I had to manually port all the math on my blog from LaTeX to do so. I think a lot of people using this feature might be moving over from a previous math implementation, so they will likely use LaTeX, but my experience is that writing new posts with Typst is way easier. In the ideal world, it would be possible to specify math on a per-page or per-block basis, but that might be a little too complex.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I've seen online as well. I don't use any math/LaTeX whatsoever myself so I don't have an opinion myself on that other than keeping things simple
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo, its about supporting legacy use cases, 95% of all math/physics papers are still written in latex, and its also the standard format for math in markdown: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/writing-mathematical-expressions. I think the main reason everyone on this thread is using typst is just the attraction to rust. But if no one on this thread actually wants support for Katex, it might be worth it to remove it for now and only add it if someone requests over typst.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the case for everyone probably, example is me, I am rather a Typst user & enthusiast (rust as well but not as much experience using it) and actually would like to use it for a documentation website involving lots of mathematics code. Typst is gaining functionality and convenience very quickly and left LaTeX already behind in most aspects (my opinion).
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personaly still use LaTeX because it is what I used untill now, and also for tikz figures and graphs (not with zola, just in my other documents). If Typst has the same math rendering quality as Katex and maintaining multiple engines is a pain, it would bother me to switch to Typst but I would do it anyways. I won't complain on this decision, I'm already grateful people made math rendering possible.
I can only see one downside of having only Typst: if people write things both in their blog and in research papers, they would need to write things in two languages each time. But I guess it's rare.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every rust rewrite has a portion of supporters purely based on the fact that it is new, fancy, and written in rust. Existence of such supporters is a terrible indicator to judge a project's actual quality, soundness of architectural decisions and overall adoption trajectory.
When typst first got out, I was very critical because it was quite young and it had a few rough edges, but I quickly flipped because it made me way faster. I wrote my 140p thesis in typst and will never go back. After only one year of learning I was able to get my hands dirty with custom commands and document introspection in a way that after 7 years of latex I was never able to come close to.
Typst does numerous things a ton better in a way that latex will never be able to: To name one, TeX is a macro processing language, and this will never change. That makes it impossible to give good syntactical error messages or editor completion (heuristics are always possible, but as soon as you have custom commands you are out the window), and you will not be able to change that. To name another, the packaging ecosystem is easy to use and growing rapidly. And we don't have to talk about compilation speed (although to be fair the static web-rendering use case is different from getting a live preview while typing, where one sees the most benefit).
All this is to say two things.
First, I guarantee you there will be a growing portion of users who will want to use typst and feel very strongly about its adoption in up-and-coming projects such as
zola.Second, LaTeX is still a requirement for most people, and will continue to be. A portion of such people are likely to cast out
zolaif they want to typeset math but don't want the burden of learning a „fancy new typesetting language that nobody uses and will probably die in a few years, anyway“. Especially in academia things are slowly moving (rightfully so), and the change in adoption will probably come with a generational change; some communities will never reach it. Something similar is happening with automated theorem proving, and something similar happened in math with the introduction of category theoretical methods. We're talking on the scale of decades here.Sure, much of this is anecdotal, and I might be wrong about my projections, but I like to think spending 6+ months doing nothing but writing typst and having had extensive LaTeX experience beforehand counts for something other than „irrelevant because opinion-based and not measurable“.
I am however not sure what you should do with this information, because I can't seriously tell someone to “just support both” without being the one who has to carry the maintenance burden.