Update: the link is now https://wentaoli.xyz/second_quantization.
I was using @MrMugame 's script a lot. Recently updated and it looks like it broke due to using “+” to concat strings rather than “..”. I provided my updated code below for anyone else who may have broken functions now.
latex = {
header = [[<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">]],
katex = js.import("https://cdn.jsdelivr.net/npm/[email protected]/+esm")
}
function latex.inline(expression)
local html = latex.katex.renderToString(expression, {
trust = true,
throwOnError = false,
displayMode = false
})
return widget.new {
html = "<span>" .. latex.header .. html .. "</span>"
}
end
function latex.block(expression)
local html = latex.katex.renderToString(expression, {
trust = true,
throwOnError = false,
displayMode = true
})
return widget.new {
html = "<span>" .. latex.header .. html .. "</span>"
}
end
slashcommand.define {
name = "math",
run = function()
editor.insertAtCursor("${latex.inline[[]]}", false, true)
editor.moveCursor(editor.getCursor() - 3)
end
}
slashcommand.define {
name = "equation",
run = function()
editor.insertAtCursor("${latex.block[[]]}", false, true)
editor.moveCursor(editor.getCursor() - 3)
end
}
Recently updated and it looks like it broke due to using “+” to concat strings rather than “..”.
Yeah, that’s expected. The Space Lua is (much more) aligned with the standard Lua 5.4 behaviour now.
Here’s my version with a really terrible trick, based on the code by @MrMugame:
Space Lua
local module = js.import("https://cdn.jsdelivr.net/npm/[email protected]/+esm")
local function render(expr, display)
return widget.new({
html = module.renderToString(expr, {
displayMode = display,
trust = true,
throwOnError = false
})
})
end
katex = {}
setmetatable(katex, {
__call = function (_, expr)
return render(expr, false)
end,
__mul = function (_, expr)
return render(expr, true)
end
})
slashCommand.define({
name = "katex",
description = "Insert inline LaTeX equation",
run = function()
editor.insertAtCursor("${katex[[|^|]]}", false, true)
end
})
slashCommand.define({
name = "katex*",
description = "Insert LaTeX equation in display style",
run = function()
editor.insertAtCursor("${katex*[[|^|]]}", false, true)
end
})
Space Style
@import url("https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css");
.sb-lua-directive-inline:has(.katex) {
border: none !important;
}
Also note that it is technically possible to store everything locally.
To do this you have to replace the urls with their corresponding base 64 version, i.e. data:text/css;base64,... and data:text/javascript;base64,....
The only remaining issue is fonts. I haven’t tried this, but if you don’t like them missing, you could modify the @font-face definitions within katex.css to replace every url(...) with e.g. url("data:font/woff2;base64,...").
Nasty… but I guess it could work.
Hi. My recent PR has enabled support for inline math $...$ and block math $$...$$. In my personal testing, everything seems to be working just fine.
Hi Qian, Thanks for the PR! This looks so interesting. However, I was thinking of the possibility of swapping the math engine for enabling support for not only LaTeX-based math expression but also the Typst one.
I am also looking into it. Do you mind if I also work with you together to enable more flexible math support?
Great!
I was missing this a lot! (And I guess, not only me.)
To support different syntaxes, the best would be to have support for inline and block attributes as Djot happen to have. (Not speaking of the fact that the attributes would solve many other needs user may have!) But while we don’t have the attributes (yet, @zef ?) we can imagine that at least for the block math we could have something like
$ math $and$$ math $$for standard LaTeX syntax and rendering variant,$typst$ math $and$$typst$ math $$for the Typst variant.
Ugly but doable.
Having the attributes would allow for much nicer solutions!
Hi, I’m not familiar with Typst. If you can help, it will really be appreciated!
What I did is just extending SilverBullet’s markdown parser to be able to parse $..$ and $$...$$, and render content between these marks using KaTeX. So maybe we can render Typst equations using something other than KaTeX? I’m not sure.
I think it is now a common practice in obsidian and quartz that user are allowed to swap the underlying math engine to parse the math block.
I want to look into how we can enable this architecture similar to quartz, which I use for publishing notes in my space.
Having an ugly new syntax just for typst math is not ergonomic nor cleaner in the long run. quartz/docs/plugins/Latex.md at v4 · jackyzha0/quartz · GitHub is the documentation for the quartz latex plugin if you need more context what I am talking about.
I still think that to gain the attributes support is the best way to go:
For inline:
Some $ inlined math ${typst} in Typst.
or
Some $ inlined math in red color ${engine=typst .red} in Typst.
For blocks:
{.blue typst}
$$
math in blue color
$$
or
{engine=typst .big}
$$
math
$$
Whatever… I love the attributes because they fill the gap we are running into here in very intuitive and truly natural manner.
Ah didn’t see there was a discussion on this PR here, but indeed I at some level really this idea, but am also not super comfortable with the idea it’s tied to katex. Having a more general mechanism that allows one to swap in or out “engines” would be nicer.
What I really liked about the “code widget” setup that we relied on more (where a plug could define the implementation of a ```latex block for instance. Or the more recent idea of using Lua expressions to do this type of stuff, like done here: GitHub - MrMugame/silverbullet-math: LaTeX math rendering for Silverbullet using KaTeX
This avoids having to “pollute” the markdown syntax with more very specific stuff, like LaTeX (or typst or whatever) formulas.
The inline and block attributes as in Djot that @mjf could be a solution, but in the context of markdown are also very much non-standard.
Is it primarily the short-hand syntax we’re after compared to the solution of embedding these in Lua expressions, or is it also compatibility with e.g. certain Obsidian (or LogSeq, not sure) plugins?
I think people will likely want to have their preferred engine used across the entire space, so annotations seem clunky and overly verbose. I want to set the space to interpret math blocks with typst or latex or w/e, not to have to type $$typst$ everytime.
I agree. It is very unlikely that a normal author would want to use latex and typst syntax simultaneously in the same space. Adding additional tag could be fancy tricks in Silverbullet, but in general not a good syntax and will hinder the inter-compatibilty of the note between silverbullet and other markdown based systems.
Me myself personally use silverbullet for most of the note taking and writing, and also I use the quartz for publishing and typst for the math expression. I don’t think my usage is a rare case so it should be considered important that we keep our syntax as close to “standard note taking markdown” as possible.
I would personally hate writing anything like ${latex.block [[ ... ]]}. We should stick to the generic syntax used by the vast majority of software: $ for inline math and $$ for blocks. Most people are accustomed to this and will use it by default.
That said, the {} attributes provide a major benefit. Imagine simply writing a paragraph in KaTeX and placing {latex} before it - it gets rendered by whichever plug hooks into that attribute. In short: while the standard dollar-sign syntax is a mandatory extension, the {} attributes streamline how plugs work, without overcomplicating the Markdown!
@zhiim in his PR confirmed what I feared, which is that adding Katex to the bundle would add about 600K, which I think is too much for the niche that it serves. So even from that perspective this doesn’t look like a great solution.
Switching to thinking out loud mode now.
One thing that SilverBullet had at some point, but I later removed, is the ability to extend the markdown parser from plugs. Support for this was very limited, just simple regular expressions were supported. I’m now thinking if it would be worth considering bringing such a feature back, because it would unlock a lot of potentially cool stuff.
If there would be a way to define two types of syntax extensions (inline and blocks) in a declarative way, e.g. with Lua, creating custom AST nodes for them, but also allowing a way to plug in custom rendering for live preview (basically turning them into Lua widgets), then something like this could be built without having to bake it into SilverBullet core. If you’d want to load an additional 600K of code for this, go ahead, but others that don’t even know what LaTeX is wouldn’t have to.
One implementation caveat is that the markdown parsing happens in synchronous JavaScript code, no asynchronous callbacks allowed. So we cannot simply pass control to Lua code, which very likely will be executed async. An obvious solution is to use regular expressions to do the parsing instead. But regexes are limited.
Concept code:
syntax.defineInline {
match = "\$(.+)\$",
node = "InlineLatex", -- ast node name
cssClass = "inline-latex", -- to style "code view"
render = function(node)
-- live preview
return widget.new { ... }
end
}
Regular expressions to do this type of parsing are somewhat limited, sadly, but I don’t immediately see good alternatives. Also not sure exactly yet how block style parsers would work.