Skip to content
This repository was archived by the owner on Jun 3, 2024. It is now read-only.

Math in dcc.Markdown #634

Closed
wants to merge 3 commits into from
Closed

Conversation

anders-kiaer
Copy link

@anders-kiaer anders-kiaer commented Sep 2, 2019

Adding this pull request to show how the dcc.Markdown component can be extended to also support rendering of LaTeX math equations using react-katex based on KaTeX, for those users who need this feature. Feel free to close if not aligned with dcc development plans.

In order to explain/describe some given data set visualized in a Dash app, it is sometimes useful to be able to include mathematical equations and symbols. Instead of making a new Dash component, this PR extends the dcc.Markdown component such that math easily can be added in between markdown text. E.g. you can do:

import dash
import dash_core_components as dcc

app = dash.Dash(__name__)

app.layout = dcc.Markdown(r'''

Below are the different Maxwell equations listed,

$$
\begin{aligned}
\nabla \cdot \mathbf{E} &= \frac {\rho} {\varepsilon_0} \\
\nabla \cdot \mathbf{B} &= 0 \\
\nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}} {\partial t} \\
\nabla \times \mathbf{B} &= \mu_0\left(\mathbf{J} + \varepsilon_0 \frac{\partial \mathbf{E}} {\partial t} \right)
\end{aligned}
$$

which form the foundation of classical electromagnetism. 

Using inline math you can describe the different parameters that goes into
the Maxwell's equations, e.g. $\varepsilon_0$ is permittivity of free space.
''')

if __name__ == '__main__':
    app.run_server(debug=True)

will result in

image

Built files are (currently) not included in the PR (in order to only show the source code changes needed). I.e. you will need to run npm install, npm run build and python setup.py install first.

Related:

@anders-kiaer anders-kiaer marked this pull request as ready for review September 5, 2019 17:35
@byronz byronz requested a review from wbrgss September 5, 2019 17:44
@byronz
Copy link
Contributor

byronz commented Sep 5, 2019

@anders-kiaer thanks for this PR, I think markdown is a logical place for maths equations.
I will leave @wbrgss and @Marc-Andre-Rivet to comment on the possible increment of bundle size (compressed): react-katex is 64.4kb and remark-math is about 1kb

We would also need an integration test to cover this feature using dash.testing. you can put the test inside a markdown folder under tests/integration. A percy snapshot might be sufficient with a more comprehensive example.

use:[
{
loader: 'url-loader'
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presumably this is for the Katex fonts... where is this documented? what does this do to the bundle size?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct @alexcjohnson 👍 I would guess this is not the optimal / final way of including KaTeX fonts wrt. bundle size and loading speed, if KaTeX support in dcc.Markdown is something worth adding. Maybe keep KaTeX as an external thing 📦 (like highlight.js is already) and load KaTeX globally?

Another thing to consider if including math in the markdown component: Should it have an argument stating if math is to be rendered or not (such that it is not a breaking change if e.g. the user writes $some text which should not be inline math$)? Or maybe a better alternative: do not use $...$ for inline math, but rather e.g. the MathJax default \(...\).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, it wouldn't be hard to end up with two $ on a line together accidentally. Thanks for bringing this up! So yeah, let's make this opt-in. Perhaps also with an option to choose new delimiters, though that's not so important if math rendering is opt-in.

Global loading and fonts frankly sounds like a headache - particularly as we need to maintain the ability to run airgapped. Perhaps the best would be to hold this PR until we finish sorting out lazy loading #616, then make sure KaTeX is only loaded when rendering math-enabled markdown? Thoughts @Marc-Andre-Rivet?

@alexcjohnson
Copy link
Collaborator

Ooh there's now a MathJax v3 that claims a big speed boost, synchronous rendering, and modern build options. I wonder if that changes our thinking about how to support math in markdown?

I love KaTeX but it doesn't have an SVG renderer so can't be used with plotly.js. We've thought about some ridiculously hacky ways to convert it to SVG (it needs to be real SVG, not a foreign object) but I'm skeptical we could really do this sufficiently robustly.

So plotly.js is going to stay on MathJax for the foreseeable future, and it'll be a project in its own right to upgrade that to v3, but if we use MathJax here we'd at least be on a path toward a single math display package for all of Dash.

Thoughts?

@renatobellotti
Copy link

renatobellotti commented Feb 5, 2020

Is there any progress?

@anders-kiaer
Copy link
Author

Is there any progress?

Not from my side at least. I agree with @alexcjohnson that MathJax 3 looks like a big improvement compared to MathJax 2, and using the same library as plotly.js is probably a good argument for using MathJax in dash-core-components. Getting math into the Dash based project at work, using MathJax, however, has not bubbled high enough up on the prioritization list for me to be able to use time on it.

Might have some time to take a look at how to use MathJax 3 instead of KaTeX, in dcc.Markdown, in ~a months time, if no one else would give it a shot before that 🚀 (and also given that @alexcjohnson still thinks it is something worth exploring).

@renatobellotti
Copy link

Thanks for your effort!

@anders-kiaer
Copy link
Author

anders-kiaer commented Mar 10, 2020

There is now one commit using KaTeX, and one using MathJax. I must say the user experience of using MathJax v3 is much better compared with MathJax v2. The MathJax documentation on direct usage of MathJax modules, within a JavaScript library together with import, is still not very extensive though I would say, so it is a bit difficult to know how to best use MathJax in those use cases. There is some information however in this issue comment: mathjax/MathJax#2194 (comment)

For now at least, this WIP PR uses a ready-made MathJax component tex-svg. The component seems to register itself to window on import.

A test example below, where user input (including math) is rendered live and dynamically (which can be tested after npm install --ignore-scripts && npm run build && pip install -e .), for those that want to try it out:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

SOME_MARKDOWN = r"""
Below are the different Maxwell equations listed,

$$
\begin{aligned}
\nabla \cdot \mathbf{E} &= \frac{\rho}{\varepsilon_0} \\
\nabla \cdot \mathbf{B} &= 0 \\
\nabla \times \mathbf{E} &= -\frac{\partial \mathbf{B}} {\partial t} \\
\nabla \times \mathbf{B} &= \mu_0\left(\mathbf{J} + \varepsilon_0 \frac{\partial \mathbf{E}} {\partial t} \right)
\end{aligned}
$$

which form the foundation of classical electromagnetism. 

Using inline math you can describe the different parameters that goes into
the Maxwell's equations, e.g. $\varepsilon_0$ is permittivity of free space.
"""

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Textarea(
            id="textarea", value=SOME_MARKDOWN, style={"width": "100%", "height": "50vh"}
        ),
        dcc.Markdown(id="user-provided-output"),
    ]
)


@app.callback(
    Output(component_id="user-provided-output", component_property="children"),
    [Input(component_id="textarea", component_property="value")],
)
def update_output_div(input_value):
    return input_value


if __name__ == "__main__":
    app.run_server(debug=True)

Remaining tasks if this is to be taken further:

  • Move import/load of MathJax to LazyLoader.
  • Configure MathJax to not typeset any existing math on page when loaded.
  • Use MathJax event system to ensure that we do not try to render before MathJax is loaded and ready.
  • Chain MathJax typeset promises?
  • Investigate if compiling/creating own MathJax component is better?
  • Pass symbols indicating block/inline math from the Dash Markdown component (as discussed earlier, we might not want $inline_math$ to be the default).
  • Boolean input argument to Dash Markdown component to turn rendering of mathematics on/off.
  • Extend test set with rendering of mathematics.

image

@anders-kiaer
Copy link
Author

Superseded by plotly/dash#1949. 🎉

@anders-kiaer anders-kiaer deleted the math_in_markdown branch March 2, 2022 13:57
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants