Math should be quotable

We have a discourse forum for a scientific software, and use the math plugin. Unfortunately, math is not quotable.

If I select some text, a Quote button appears:

Clicking it results in the following:

[quote]
definition of the closeness centrality:

ci=1N−1N∑j=11dij c_i = \frac{1}{N-1} \sum_{j=1}^N\frac{1}{d_{ij}}
[/quote]

Notice that in the quotation, the math appeared as a weird textual version (unreadable) followed by the actual LaTeX source, but lacking the $ delimiters.

I assume that there is no fix for this currently. It would be nice if this could be improved in the future.

8 Likes

This is still occurring and also happens with inline math, as reported by a member on our instance:

3 Likes

Note, I agree completely we should clean this up, but it feels extremely complicated.

I am putting a pr-welcome on this in case someone with advances skills wants to give it a shot.

7 Likes

I had a look into quoting math (I’m considering writing a PR or contributing to one).

Documenting some initial thoughts and findings here.

So my initial impression is one of these points might be patchable:

  • toMarkdown matches elements against various html tags; mathjax uses custom elements I think that are detectable (mb not SVG rendering), mb katex does too?
  • selectedText does some matching for elements like code blocks and onebox – might be able to detect mathjax/katex elements and transform them? (Note: probably a good idea to have a hook or something here so the logic for transformation can be added to the discourse-math plugin instead of the main discourse codebase).
  • the other points don’t seem as suitable as either of these two.

Some other thoughts on approaches and how to get the raw math.

Depending on how katex / mathjax+svg[1] work, every instance of math should have a root HTML element I think (possibly multiple b/c there are some hidden elements for accessibility and things). So if we know the places that math exists, we might be able to parse the raw post for instances of content delimited with $ (but interpreters are tricky – mb there’s already one in the codebase?)

Alternatively, mathjax at least has a right-click > view-tex esq feature (which should be the literal string between $ pairs); I am not sure how to hook in to that, but if there’s a way to do that for mathjax v2, v3, and katex, then it should be possible to use that to replace selected formatted math with $...$ (note: that works for inline math, need to cover the case for the other kind, too – which uses $$...$$ or [/.../] delimiters.)

Problems atm:

  • IDK how to implement a hook or whether there’s something better to use / a better way of doing it in the discourse codebase.
  • Several unknowns about mathjax/katex that need to be investigated.
    • HTML elements – how to detect them reliably?
    • How to get raw TeX code?

If anyone has any other ideas for where/what to patch, other possible implementation methods, or whatever, then please post them.


  1. I think mb mathjax+svg isn’t supported, so that mb isn’t an issue ↩︎

5 Likes

Discourse Math was heavily upgraded in January 2026 to a new official core-bundled version, with MathJax 4.1 as default and KaTeX as an alternative. The plugin docs also note that KaTeX includes the CopyTex extension for copying LaTeX source.

Would it be worthwhile adding a small theme-component patch that improves quote selection for math-heavy posts?

For MathJax-heavy sites like Physics with Ethan, the most practical idea is:
1. intercept the selected HTML before Discourse turns it back into Markdown,
2. find math wrapper elements,
3. replace them with their original TeX source wrapped in $...$ or $$...$$,
4. then let Discourse continue building the quote.

Here is a potential theme-component script i would start off with, if it’s worthwhile modifying the Horizon (or Default) theme’s </head> or JS area.


<script type="text/discourse-plugin" version="1.0">
  apiInitializer("1.34.0", (api) => {
    function texFromMathElement(el) {
      // MathJax v3/v4 commonly stores the source in an annotation child.
      const annotation =
        el.querySelector('annotation[encoding="application/x-tex"]') ||
        el.querySelector('annotation');

      if (annotation?.textContent?.trim()) {
        const tex = annotation.textContent.trim();

        // Heuristic: block math wrappers are often display containers.
        const isBlock =
          el.tagName === "MJX-CONTAINER" &&
          (el.getAttribute("display") === "true" ||
            el.getAttribute("display") === "block");

        return isBlock ? `$$\n${tex}\n$$` : `$${tex}$`;
      }

      return null;
    }

    function patchMathInFragment(fragment) {
      const candidates = fragment.querySelectorAll(
        "mjx-container, .math, .katex, .MathJax"
      );

      candidates.forEach((node) => {
        const replacement = texFromMathElement(node);
        if (!replacement) {
          return;
        }

        const textNode = document.createTextNode(replacement);
        node.replaceWith(textNode);
      });

      return fragment;
    }

    api.modifyClass("component:quote-button", {
      pluginId: "ethan-math-quote-fix",

      _selectionChanged() {
        this._super(...arguments);

        try {
          const selection = window.getSelection();
          if (!selection || selection.rangeCount === 0) {
            return;
          }

          const range = selection.getRangeAt(0);
          const fragment = range.cloneContents();
          patchMathInFragment(fragment);

          const container = document.createElement("div");
          container.appendChild(fragment);

          // Replace the buffer that Discourse later converts into quote markdown.
          if (this.quoteState) {
            this.quoteState.buffer = container.innerHTML;
          }
        } catch (e) {
          // Fail quietly so normal quoting still works.
          console.warn("Math quote patch failed:", e);
        }
      },
    });
  });
</script>

A few important caveats:
• This is a practical patch, not something I found already published by Meta. The underlying strategy is inferred from the quote path going through the selected HTML and toMarkdown.
• It is most likely to help when i’m using the MathJax provider, which is the current default in Discourse Math, and the option i usually use.
• The script may need small selector tweaks depending on whether my rendered math ends up as mjx-container, .MathJax, or another wrapper. I would test on a staging site, that would be a new standard install.
• It is unlikely to be perfect for every edge case, especially mixed inline/block selections or nested quoted material.

Because Discourse now officially supports KaTeX with CopyTex, another route is to switch provider if my main pain point is copying LaTeX source rather than rendering fidelity. That would not automatically solve quote-selection, but it does mean the rendered math already has a more copy-friendly path built in.

For my MathJax heavy site, Physics with Ethan, i’m currently thinking:
• keep MathJax unless someone suggests i could change to KaTex? ,
• maybe add a quote-selection patch to the Horizon theme, from the above (using a staging site).
• and treat it as a local quality-of-life fix until there is a proper upstream solution. This 2020 feature topic still being open in 2026 suggests upstream may remain slow here.

2 Likes