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: diff --git a/docs/index.md b/docs/index.md index 123ab6a..6f3f679 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,16 +27,38 @@ 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 ```{eval-rst} @@ -118,13 +140,80 @@ html_string = md.render("some *Markdown*") ``` ```{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.

+``` + +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. + +**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..1bf5c87 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 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. + 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