RyotaUshio / obsidian-mathjax-preamble-manager

Use preamble in Obsidian / switch preambles note to note
MIT License
15 stars 0 forks source link

Happy to help #3

Open mayurankv opened 9 months ago

mayurankv commented 9 months ago

Hey, I've been trying to fix MathJax preambles in Obsidian for a while! If you need any help with this plugin, please do let me know!

RyotaUshio commented 9 months ago

Thanks! To be honest, I don't know about MathJax very much so your help will be definitely appreciated.

And I do have a problem right now. Do you have any ideas on how to "cancel" an already-defined command? I wonder if this can be achieved by a more sophisticated approach than the current one.

The current approach of this plugin is very naive.

Namely, before every markdown post-processing or CodeMirror's update, this plugin

  1. Determines which preamble file to load based on the current note path (resolvePreamble)
  2. Checks if the preamble to be loaded is different from the previous one
  3. If so, it just executes renderMath(preamble, false), which is equivalent to MathJax.tex2chtml(preamble, {display: false}).

This is super simple, but this way we can switch the definition of a single command (e.g. \P{...}) between two (e.g. \mathbb{P} \left( ... \right) and \mathrm{Pr} \left[ ... \right]) or more based on note paths.

But the problem is that this approach doesn't allow us to cancel a command that has been already defined by a preamble but not contained in another one. For example,

% preamble 1: applied to note1.md
\newcommand{\foo}{\mathbb{FOO}}
\newcommand{\bar}{\mathbb{BAR}}
% preamble 2: applied to note2.md
\newcommand{\foo}{\mathbf{FOO}}

I want \bar not to work in note2.md because preamble 2 doesn't contain it, but with the current approach, it does work both in note1.md & in note2.md if we load note1.md first.

Do you have any suggestions, including replacing the entire approach with a new one?

RyotaUshio commented 9 months ago

Also I'd appreciate any other suggestions not necessarily related to the point above.

mayurankv commented 9 months ago

For me, the main things I would like to do are:

I think points 2-4 are literally only possible with a more exposed MathJax. I'm no expert but I've tried hacking the config of the existing MathJax object and wasn't able to really get anywhere with those points. I guess point 1 is more relevant to this discussion.

I'm not actually aware of how to remove commands, it appears to be the big issue with this kind of approach. I've asked a question on the MathJax github itself in case anyone can point us in the right direction. I'll let you know is anything helpful comes up. The only alternative approach I can see is loading your own MathJax instance but I'm not sure how Obsidian would let you do that. Does it let you override the MathJax object? There isn't sandboxing so this may be possible and would actually help with points 2-4. I've not tried to do this inside a plugin to overwrite MathJax so please let me know how it goes if you try it. I may also try it if I find the time soon.

RyotaUshio commented 9 months ago

Finer control over preambles

Do you have any specific features that you want to have other than switching preambles from note to note?

Changing maths delimiters so that it can be consistent with pandoc etc.

This is extremely difficult because it's not MathJax but Obsidian's markdown parsers that determine what delimiters to use.

(Indeed, you can find this in Obsidian's app.js:

            var iA = Db("/lib/mathjax/tex-chtml-full.js", {
                before: function () {
                    window.MathJax = {
                        tex: {
                            inlineMath: [],
                            displayMath: [],
                            processEscapes: !1,
                            processEnvironments: !1,
                            processRefs: !1
                        },
                        startup: {
                            typeset: !1
                        },
                        options: {
                            enableMenu: !1,
                            menuOptions: {
                                settings: {
                                    renderer: "CHTML"
                                }
                            },
                            renderActions: {
                                assistiveMml: []
                            },
                            safeOptions: {
                                safeProtocols: {
                                    http: !0,
                                    https: !0,
                                    file: !0,
                                    javascript: !1,
                                    data: !1
                                }
                            }
                        }
                    },
                        localStorage.removeItem("MathJax-Menu-Settings")
                }
            });

Note that it says inlineMath: [], displayMath: [].)

And here's a bad news. Live preview seems to use HyperMD, and its [documentation]() clearly says this:

math

🎨 Type: boolean 📦 Default: true

Parse TeX formula wrapped by $ or $$.

🚩 Example:

Inline Formula: $\LaTeX$ and $$y=kx+b$$

Display Formula: $$ \begin{bmatrix} \cos \theta && -\sin \theta \ \sin \theta && \cos \theta \end{bmatrix} $$

And to make matters worse, reading view probably uses a different parser from live preview and I don't know what it is. Even if I knew, it would be hard to modify how it works.

I've asked a question on the MathJax github itself in case anyone can point us in the right direction. I'll let you know is anything helpful comes up.

Wow thanks! Much appreciated.

RyotaUshio commented 9 months ago

Does it let you override the MathJax object?

I'm not sure if we can overwrite the global MathJax object without any issues, but we can at least modify what the renderMath function (which reading view & live preview uses to render latex) does by monkey-patching MathJax.tex2chtml. It's what I did in my plugin called Auto-\displaystyle Inline Math.

https://github.com/RyotaUshio/obsidian-auto-displaystyle-inline-math/blob/e080e89b465b71e2c473263f0593ee2348bebdc9/main.ts#L51-L60

Perhaps we might be able to just switch MathJax instances (and hence preambles) based on note paths using this technique, if we can somehow load multiple ones.

BTW, I think some parts of MathJax's config can be changed afterward, as I mentioned in this post on the forum: https://forum.obsidian.md/t/links-from-within-math-blocks/69050/4?u=ush

RyotaUshio commented 9 months ago

Hey, I've succeeded in loading another MathJax object!

https://github.com/RyotaUshio/obsidian-mathjax-preamble-manager/blob/519b247f5bf1830398b9a292393534f6349af3e1/src/manager.ts#L32-L71

image
RyotaUshio commented 9 months ago

And now we can remove commands as well!

https://github.com/RyotaUshio/obsidian-mathjax-preamble-manager/assets/72342591/f576a23b-80df-4945-9336-399a47d37b6c

RyotaUshio commented 9 months ago

Now we can modify the MathJax config as well.

For example, the default config doesn't allow us to use the javascript: & obsidian: protocols, and tags are displayed on the right side, but we can do this:

https://github.com/RyotaUshio/obsidian-mathjax-preamble-manager/assets/72342591/cf6a36eb-158c-4d74-972e-498428cbdcca

RyotaUshio commented 9 months ago

Be able to inject custom startupReady commands etc

Is this what you wanted?

https://github.com/RyotaUshio/obsidian-mathjax-preamble-manager/assets/72342591/944168b2-6acf-4f76-b830-7492cf3a84fb