remarkjs / remark-math

remark and rehype plugins to support math
https://remark.js.org
MIT License
383 stars 57 forks source link

rehype-mathjax: CHTML not rendering #85

Closed FunkMonkey closed 2 months ago

FunkMonkey commented 1 year ago

Initial checklist

Affected packages and versions

4.0.3

Link to runnable example

No response

Steps to reproduce

import React from 'react';
import ReactMarkdown from 'react-markdown';
import rehypeMathjaxChtml from 'rehype-mathjax/chtml';
import remarkMath from 'remark-math';

export interface IExpressionProps {
  expression: string;
  inline?: boolean;
}

export function Expression({ expression, inline }: IExpressionProps): React.ReactElement {
  const text = inline ? `$${expression}$` : `$$${expression}$$`;
  return (
    <ReactMarkdown
      remarkPlugins={[remarkMath]}
      rehypePlugins={[
        [
          rehypeMathjaxChtml,
          {
            scale: 4,
            chtml: {
              fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3.2.2/es5/output/chtml/fonts/woff-v2',
            },
          },
        ],
      ]}
    >
      {text}
    </ReactMarkdown>
  );
}

Runtime: Building via Vite for the browser on Node18.

Expected behavior

Equations should be rendered

Actual behavior

Equations are not rendered with rehypeMathjaxChtml, but are rendered when using the default SVG renderer instead.

The problem is most likely that the generated HTML-code contains classname instead of class, e.g.:

<mjx-container classname="MathJax" jax="CHTML">
  <mjx-math classname="MJX-TEX">
    <mjx-mo classname="mjx-n">
      <mjx-c classname="mjx-c3D"></mjx-c>
    </mjx-mo>
    <mjx-mfrac space="4">
      <mjx-frac>
        <mjx-num>
          <mjx-nstrut></mjx-nstrut>
          <mjx-mstyle size="s" style="color: rgb(76, 175, 80);">
            <mjx-mtext classname="mjx-n">
              <mjx-c classname="mjx-c41"></mjx-c>
              <mjx-c classname="mjx-c6E"></mjx-c>
              <mjx-c classname="mjx-c7A"></mjx-c>
              <mjx-c classname="mjx-c61"></mjx-c>
              <mjx-c classname="mjx-c68"></mjx-c>
              <mjx-c classname="mjx-c6C"></mjx-c>
              <mjx-c classname="mjx-c20"></mjx-c>
              <mjx-c classname="mjx-c64"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c72"></mjx-c>
              <mjx-c classname="mjx-c20"></mjx-c>
              <mjx-c classname="mjx-c45"></mjx-c>
              <mjx-c classname="mjx-c6C"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c6D"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c6E"></mjx-c>
              <mjx-c classname="mjx-c74"></mjx-c>
              <mjx-c classname="mjx-c65"></mjx-c>
              <mjx-c classname="mjx-c20"></mjx-c>
              <mjx-c classname="mjx-c69"></mjx-c>
              <mjx-c classname="mjx-c6E"></mjx-c>
              <mjx-c classname="mjx-cA0"></mjx-c>
            </mjx-mtext>
            <mjx-mi classname="mjx-i">
              <mjx-c classname="mjx-c1D43E TEX-I"></mjx-c>
            </mjx-mi>
            <mjx-mo classname="mjx-n">
              <mjx-c classname="mjx-c2229"></mjx-c>
            </mjx-mo>
            <mjx-mi classname="mjx-i">
              <mjx-c classname="mjx-c1D445 TEX-I"></mjx-c>
            </mjx-mi>
          </mjx-mstyle>
        </mjx-num>
        <mjx-dbox>
          <mjx-dtable>
            <mjx-line></mjx-line>
            <mjx-row>
              <mjx-den>
                <mjx-dstrut></mjx-dstrut>
                <mjx-mstyle size="s" style="color: rgb(63, 81, 181);">
                  <mjx-mtext classname="mjx-n">
                    <mjx-c classname="mjx-c41"></mjx-c>
                    <mjx-c classname="mjx-c6E"></mjx-c>
                    <mjx-c classname="mjx-c7A"></mjx-c>
                    <mjx-c classname="mjx-c61"></mjx-c>
                    <mjx-c classname="mjx-c68"></mjx-c>
                    <mjx-c classname="mjx-c6C"></mjx-c>
                    <mjx-c classname="mjx-c20"></mjx-c>
                    <mjx-c classname="mjx-c64"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c72"></mjx-c>
                    <mjx-c classname="mjx-c20"></mjx-c>
                    <mjx-c classname="mjx-c45"></mjx-c>
                    <mjx-c classname="mjx-c6C"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c6D"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c6E"></mjx-c>
                    <mjx-c classname="mjx-c74"></mjx-c>
                    <mjx-c classname="mjx-c65"></mjx-c>
                    <mjx-c classname="mjx-c20"></mjx-c>
                    <mjx-c classname="mjx-c69"></mjx-c>
                    <mjx-c classname="mjx-c6E"></mjx-c>
                    <mjx-c classname="mjx-cA0"></mjx-c>
                  </mjx-mtext>
                  <mjx-mi classname="mjx-i">
                    <mjx-c classname="mjx-c1D445 TEX-I"></mjx-c>
                  </mjx-mi>
                </mjx-mstyle>
              </mjx-den>
            </mjx-row>
          </mjx-dtable>
        </mjx-dbox>
      </mjx-frac>
    </mjx-mfrac>
  </mjx-math>
</mjx-container>

I am not quite sure if this is a bug of rehype-mathjax or mathjax itself. Or does CHTML only work with rehype-sanitize, e.g. see README?

Thanks!

Runtime

Other (please specify in steps to reproduce)

Package manager

yarn 2

OS

Windows

Build and bundle tools

Vite

ChristianMurphy commented 1 year ago

Thanks for reaching out @FunkMonkey! 👋

Or does CHTML only work with rehype-sanitize

While it is a good idea to use rehype-sanitize, it is not required for this project.

I am not quite sure if this is a bug of rehype-mathjax or mathjax itself.

Given that the SVG renderer works fine, but the CHTML one does not. Runnable demo: https://codesandbox.io/s/eloquent-moore-kc84ny?file=/src/app.tsx

And that the code between SVG: https://github.com/remarkjs/remark-math/blob/main/packages/rehype-mathjax/svg.js and CHTML https://github.com/remarkjs/remark-math/blob/main/packages/rehype-mathjax/chtml.js is nearly identical except which mathjax function they use, and validation for a required option. I tend to think this is a bug in MathJax itself.

/cc @tani in case you have any additional ideas on what may be happening in the chtml

ChristianMurphy commented 1 year ago

Also cross linking a recent discussion in react-markdown running into the same issue https://github.com/remarkjs/react-markdown/issues/745

wooorm commented 1 year ago

I think this has to do with React supporting properties (className) on HTML/SVG elements, but not on MathML (or custom, or unknown) elements. We support className everywhere.

Googling MathML + React gives many bugs, e.g., https://stackoverflow.com/questions/51319758/react-16-html-attributes-with-mathml-tags.

MathML was mostly abandoned for years, but recently got updates and implementations. Hopefully React will add/improve MathML support.

edward1127 commented 1 year ago

Do you have any updates on how to solve it? I've encountered the issue where the rendered SVG is not inline. I suspect it might be related to the same className issue. image

ChristianMurphy commented 1 year ago

Welcome @edward1127! 👋 Did you get a chance to read @wooorm's comment right before yours? In particular:

MathML was mostly abandoned for years, but recently got updates and implementations. Hopefully React will add/improve MathML support.

We don't have an update on the remark side, because remark isn't causing the issue. You could file an issue with React (https://github.com/facebook/react/issues) or reach out to one of the React communities (https://react.dev/community) for more specific ideas. Consider sharing a link to the discussion you start in the react community here, so others can find it and use it as reference.

edward1127 commented 1 year ago

Thanks for the reply. I just reposted your summary https://github.com/vercel/next.js/discussions/54852

ChristianMurphy commented 1 year ago

@edward1127 that link looks to be the question from #81? Do you believe this and #81 are the same issue? I tend to think they are separate, one in React in one in Next, but happy to hear other perspectives.

edward1127 commented 1 year ago

yep, you are right. Just test it out with React, and everything works fine in react for #81 .

GeorgeNabilPro commented 1 year ago

I countered this issue, and I think it can be worked around by making a custom rehype plugin to replace each 'classname' property in the hast with 'class'. However, it may not be an efficient method. But if you really want to solve the issue I think this solution deserves a try.

wooorm commented 1 year ago

I don‘t think that works. Whether it works or not it would break lots of things.

If you really want to solve this issue, see the existing comments on this discussion and solve the root cause, or use a working alternative: do not use the CHTML rendered, or use rehype-katex.

wooorm commented 2 months ago

Whether this works or not has to do with react/preact/etc. I have confirmed this works in react@rc. This makes sense, because these are custom elements, and react 19 is supposed to add proper support for them.

Closing as this is/will be solved externally.

Meanwhile, while custom elements do not work, you can always use a non-custom-element version of math.

github-actions[bot] commented 2 months ago

Hi! This was closed. Team: If this was fixed, please add phase/solved. Otherwise, please add one of the no/* labels.