diff --git a/panel/models/mathjax.py b/panel/models/mathjax.py index cec8bbb490..a448ed315c 100644 --- a/panel/models/mathjax.py +++ b/panel/models/mathjax.py @@ -1,23 +1,10 @@ """ Defines a custom MathJax bokeh model to render text using MathJax. """ -from bokeh.models import Markup +from bokeh.models import Div -class MathJax(Markup): +class MathJax(Div): """ A bokeh model that renders text using MathJax. """ - - __javascript__ = [ - "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML" - ] - - __js_skip__ = {'MathJax': __javascript__} - - __js_require__ = { - 'paths': { - 'mathjax': "//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML" - }, - 'shim': {'mathjax': {'exports': "MathJax"}} - } diff --git a/panel/models/mathjax.ts b/panel/models/mathjax.ts index 027e9a0c2a..cd300f5897 100644 --- a/panel/models/mathjax.ts +++ b/panel/models/mathjax.ts @@ -14,7 +14,21 @@ export class MathJaxView extends PanelMarkupView { override render(): void { super.render() - this.container.innerHTML = this.has_math_disabled() ? this.model.text : this.process_tex(this.model.text) + const text = this.model.text + const tex_parts = this.provider.MathJax.find_tex(text) + const processed_text: string[] = [] + + let last_index: number | undefined = 0 + for (const part of tex_parts) { + processed_text.push(text.slice(last_index, part.start.n)) + processed_text.push(this.provider.MathJax.tex2svg(part.math, {display: part.display}).outerHTML) + last_index = part.end.n + } + if (last_index! < text.length) { + processed_text.push(text.slice(last_index)) + } + + this.container.innerHTML = processed_text.join("") } } diff --git a/panel/pane/equation.py b/panel/pane/equation.py index afcd59f6e3..d988415ead 100644 --- a/panel/pane/equation.py +++ b/panel/pane/equation.py @@ -4,7 +4,6 @@ """ from __future__ import annotations -import re import sys from typing import ( @@ -24,15 +23,6 @@ from bokeh.model import Model -def is_sympy_expr(obj: Any) -> bool: - """Test for sympy.Expr types without usually needing to import sympy""" - if 'sympy' in sys.modules and 'sympy' in str(type(obj).__class__): - import sympy # type: ignore - if isinstance(obj, sympy.Expr): - return True - return False - - class LaTeX(ModelPane): r""" The `LaTeX` pane allows rendering LaTeX equations. It uses either @@ -71,20 +61,13 @@ class LaTeX(ModelPane): @classmethod def applies(cls, obj: Any) -> float | bool | None: - if is_sympy_expr(obj) or hasattr(obj, '_repr_latex_'): + if hasattr(obj, '_repr_latex_'): return 0.05 elif isinstance(obj, str): return None else: return False - def _process_param_change(self, params) -> dict[str, Any]: - if self.renderer == "mathjax": - # Replace $$math$$ with \[math\] and $math$ with \(math\) - msg = re.sub(r"(\$\$)(.*?)(\$\$)", r"\[\2\]", params["object"]) - params["object"] = re.sub(r"(\$)(.*?)(\$)", r"\(\2\)", msg) - return super()._process_param_change(params) - def _get_model_type(self, root: Model, comm: Comm | None) -> type[Model]: module = self.renderer if module is None: @@ -92,6 +75,7 @@ def _get_model_type(self, root: Model, comm: Comm | None) -> type[Model]: module = 'mathjax' else: module = 'katex' + self.renderer = module model = 'KaTeX' if module == 'katex' else 'MathJax' return lazy_load(f'panel.models.{module}', model, isinstance(comm, JupyterComm), root) @@ -110,7 +94,6 @@ def _transform_object(self, obj: Any) -> dict[str, Any]: obj = '' elif hasattr(obj, '_repr_latex_'): obj = obj._repr_latex_() - elif is_sympy_expr(obj): - import sympy - obj = r'$'+sympy.latex(obj)+'$' + if self.renderer == 'mathjax' and obj.startswith('$') and not obj.startswith('$$'): + obj = f'${obj}$' return dict(object=obj) diff --git a/panel/tests/pane/test_equation.py b/panel/tests/pane/test_equation.py index 54c2bd5b80..e1468566b8 100644 --- a/panel/tests/pane/test_equation.py +++ b/panel/tests/pane/test_equation.py @@ -24,7 +24,7 @@ def test_latex_mathjax_pane(document, comm): assert pane._models[model.ref['id']][0] is model assert type(model).__name__ == 'MathJax' # assert model.text == r"$\frac{p^3}{q}$" - assert model.text == r"\(\frac{p^3}{q}\)" + assert model.text == r"$$\frac{p^3}{q}$$" # Cleanup pane._cleanup(model) diff --git a/panel/tests/ui/pane/test_equation.py b/panel/tests/ui/pane/test_equation.py new file mode 100644 index 0000000000..e31dd53e00 --- /dev/null +++ b/panel/tests/ui/pane/test_equation.py @@ -0,0 +1,24 @@ +import pytest + +pytest.importorskip("playwright") + +from playwright.sync_api import expect + +from panel.pane import LaTeX +from panel.tests.util import serve_component + +pytestmark = pytest.mark.ui + +def test_latex_mathjax_renderer(page): + ltx = LaTeX('$1+1$', renderer='mathjax') + + serve_component(page, ltx) + + expect(page.locator('mjx-container')).to_have_count(1) + +def test_latex_katex_renderer(page): + ltx = LaTeX('$1+1$', renderer='katex') + + serve_component(page, ltx) + + expect(page.locator('.katex-html')).to_have_count(1)