From ee7eecf389291dcfa0e90b4063a132c364a2651c Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 9 Nov 2026 18:53:08 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=94=A7=20MAINTAIN:=20Build=20the=20do?= =?UTF-8?q?cs=20on=20RTD=20with=20Python=203.13?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Python 3.10 the `rtd` extra resolves markdown-it-py 3.0.0, myst-parser 4.0.1 and Sphinx 8.1, because myst-parser 5 needs Python 3.11 or later. On 3.13 it resolves markdown-it-py 4.2.0, myst-parser 5.1.0 and Sphinx 9.2, so the published docs are built against the current stack. Also move the build image to ubuntu-24.04. --- .readthedocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index f0758f4..4f02b81 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,9 +1,9 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.10" + python: "3.13" python: install: From 1f3da1e5416919b904836cb0ae52e10d4d3ec967 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 9 Nov 2026 18:53:37 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9A=20DOCS:=20Replace=20the=20plac?= =?UTF-8?q?eholder=20usage=20example=20with=20a=20runnable=20one?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The example imported `plugin1, plugin2` from `mdit_py_plugins`, which do not exist: each plugin lives in its own submodule. Show real imports, a chain of `use` calls and a keyword option, and say where the options are documented. --- docs/index.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/index.md b/docs/index.md index 123ab6a..fe8d9d0 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,15 +27,37 @@ See ## mdit-py-plugins package -The [`mdit_py_plugins`](https://github.com/executablebooks/mdit-py-plugins), contains a number of common plugins. -They can be chained and loaded *via*: +The [`mdit_py_plugins`](https://github.com/executablebooks/mdit-py-plugins) package contains a number of common plugins. +Each plugin is a function in its own submodule (for example `mdit_py_plugins.footnote.footnote_plugin`), +so import it from there; the top-level package does not re-export them. + +Load a plugin with `MarkdownIt.use`, passing any options as keyword arguments. +`use` returns the `MarkdownIt` instance, so the calls can be chained: ```python from markdown_it import MarkdownIt -from mdit_py_plugins import plugin1, plugin2 -md = MarkdownIt().use(plugin1, keyword=value).use(plugin2, keyword=value) -html_string = md.render("some *Markdown*") -``` +from mdit_py_plugins.footnote import footnote_plugin +from mdit_py_plugins.front_matter import front_matter_plugin +from mdit_py_plugins.tasklists import tasklists_plugin + +md = ( + MarkdownIt("commonmark") + .use(front_matter_plugin) + .use(footnote_plugin) + .use(tasklists_plugin, enabled=True) +) +text = """\ +--- +title: Example +--- +- [x] Some *Markdown*[^1] + +[^1]: A footnote. +""" +html_string = md.render(text) +``` + +The options of each plugin are listed in its section below. ## Front-Matter From fc3f368104bf6c1e4e3ce9fb5bfbc77e25cab5cd Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 9 Nov 2026 18:55:03 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9A=20DOCS:=20Document=20how=20dol?= =?UTF-8?q?larmath=20output=20is=20rendered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit State the HTML each dollarmath token renders to and the `display_mode` its renderer receives: the tag (`div` or `span`) says how to display the math, and the class (`inline` or `block`) says where it was written, as in markdown-it-dollarmath. So inline `$$x$$` is a `
`. Add a "Rendering dollarmath output" section with the two usual workflows: a `renderer=` that adds MathJax/KaTeX delimiters (escaping the content, which a custom renderer must do itself), and a browser script that typesets the elements with KaTeX, taking the display mode from the tag. Also say how to change the label anchor and the tags or classes. No rendered output changes. --- docs/index.md | 68 ++++++++++++++++++++++++++++- mdit_py_plugins/dollarmath/index.py | 21 ++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index fe8d9d0..974f6ce 100644 --- a/docs/index.md +++ b/docs/index.md @@ -140,13 +140,77 @@ The options of each plugin are listed in its section below. ``` ```{eval-rst} -.. autofunction:: mdit_py_plugins.dollarmath.dollarmath_plugin +.. autofunction:: mdit_py_plugins.amsmath.amsmath_plugin ``` ```{eval-rst} -.. autofunction:: mdit_py_plugins.amsmath.amsmath_plugin +.. autofunction:: mdit_py_plugins.dollarmath.dollarmath_plugin +``` + +### Rendering dollarmath output + +`dollarmath_plugin` does not typeset math: it wraps each piece of math in an HTML element, listed above. +The tag says how to display the math, and the class says where it was written. +So inline `$$x$$` (with `double_inline=True`) becomes `
`: +display math written inside a paragraph. +Pick the display mode from the tag (`div` or `span`), not from the class. + +There are two common ways to typeset the output. + +**Add delimiters on the server, with `renderer=`.** +The `renderer` function receives the math content and `{"display_mode": bool}`, +and returns the HTML to put inside the element. +Wrapping the content in `\(...\)` or `\[...\]` lets MathJax (with its default configuration) +or KaTeX's auto-render extension find it in the page: + +```python +from markdown_it import MarkdownIt +from markdown_it.common.utils import escapeHtml +from mdit_py_plugins.dollarmath import dollarmath_plugin + + +def render_tex(content: str, options: dict) -> str: + content = escapeHtml(content) + if options["display_mode"]: + return rf"\[{content}\]" + return rf"\({content}\)" + + +md = MarkdownIt().use(dollarmath_plugin, double_inline=True, renderer=render_tex) +html_string = md.render("Inline $aInline \(a<b\) and
\[x^2\]
in a paragraph.

+``` + +The returned string is inserted as is, so escape the content, as above, unless it comes from a trusted source +(the default renderer escapes it for you). +The same function can instead return fully typeset output, such as MathML from a converter library. + +**Typeset the elements in the browser.** +Without a `renderer`, each element holds the escaped LaTeX source, +and a script can typeset the elements in place, for example with KaTeX: + +```javascript +for (const el of document.querySelectorAll("span.math, div.math")) { + // a labelled block starts with its permalink anchor: keep it out of the math + const label = el.querySelector(".mathlabel"); + if (label) label.remove(); + katex.render(el.textContent.trim(), el, { + displayMode: el.tagName === "DIV", + throwOnError: false, + }); + if (label) el.prepend(label); +} ``` +To change the label anchor, pass `label_renderer=`. +To change the tags or classes themselves, replace the render rule for a token type +with `md.add_render_rule` after loading the plugin. + ## Subscripts ```{eval-rst} diff --git a/mdit_py_plugins/dollarmath/index.py b/mdit_py_plugins/dollarmath/index.py index 57ba590..38b3c8a 100644 --- a/mdit_py_plugins/dollarmath/index.py +++ b/mdit_py_plugins/dollarmath/index.py @@ -49,9 +49,28 @@ def dollarmath_plugin( :param label_normalizer: Function to normalize the label, by default replaces whitespace with `-` :param renderer: Function to render content: `(str, {"display_mode": bool}) -> str`, - by default escapes HTML + by default escapes HTML. + Its return value is placed inside the element as is, + so a custom renderer must escape the content itself. :param label_renderer: Function to render labels, by default creates anchor + The plugin does not typeset the math. + Each token becomes an HTML element holding the rendered content, + and typesetting is left to a library such as MathJax or KaTeX: + + - ``math_inline`` (``$a$``): + ````, with ``display_mode=False`` + - ``math_inline_double`` (``$$a$$`` inside a paragraph, with ``double_inline``): + ``
``, with ``display_mode=True`` + - ``math_block`` (``$$a$$`` on its own lines): + ``
``, with ``display_mode=True`` + - ``math_block_label`` (``$$a$$ (eq1)``): + ``
``, starting with the label anchor, + with ``display_mode=True`` + + The tag says how to display the math (``div`` for display, ``span`` for inline), + and the class says where it was written (``inline`` inside a paragraph, + ``block`` on its own lines), as in executablebooks/markdown-it-dollarmath. """ if label_normalizer is None: label_normalizer = lambda label: re.sub(r"\s+", "-", label) # noqa: E731 From 8a2595cfebc6c84337c7891df7e6b702c7412052 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Mon, 9 Nov 2026 19:06:36 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9A=20DOCS:=20Clarify=20dollarmath?= =?UTF-8?q?=20renderer=20escaping=20and=20inline=20div=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The renderer docstring now says a custom renderer is responsible for escaping, matching the docs, which allow trusted or fully typeset output. The docs also note that browsers close the paragraph before an inline $$ div. --- docs/index.md | 3 +++ mdit_py_plugins/dollarmath/index.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index 974f6ce..6f3f679 100644 --- a/docs/index.md +++ b/docs/index.md @@ -186,6 +186,9 @@ which gives:

Inline \(a<b\) and

\[x^2\]
in a paragraph.

``` +A browser closes the `

` before the `

`, so in the page the text after inline `$$...$$` +falls outside the paragraph: allow for that when styling or selecting this output. + The returned string is inserted as is, so escape the content, as above, unless it comes from a trusted source (the default renderer escapes it for you). The same function can instead return fully typeset output, such as MathML from a converter library. diff --git a/mdit_py_plugins/dollarmath/index.py b/mdit_py_plugins/dollarmath/index.py index 38b3c8a..1bf5c87 100644 --- a/mdit_py_plugins/dollarmath/index.py +++ b/mdit_py_plugins/dollarmath/index.py @@ -51,7 +51,7 @@ def dollarmath_plugin( :param renderer: Function to render content: `(str, {"display_mode": bool}) -> str`, by default escapes HTML. Its return value is placed inside the element as is, - so a custom renderer must escape the content itself. + so a custom renderer is responsible for escaping it (the default renderer does). :param label_renderer: Function to render labels, by default creates anchor The plugin does not typeset the math.