From 281dc458acf60a689dc41c3218af09dd4b585660 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 09:18:42 +0000 Subject: [PATCH 1/7] Initial plan From 119b6134a1943467ac956b99eebc4efba09a39e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 09:23:46 +0000 Subject: [PATCH 2/7] add mermaid sphinx extension with tests and docs Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/d8720735-e1ac-42f5-8f74-5cb189420a5e Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> --- _doc/api/index.rst | 1 + _doc/api/mermaid.rst | 77 ++++++ _doc/conf.py | 2 + .../ut_mermaid/test_mermaid_extension.py | 127 +++++++++ sphinx_runpython/mermaid/__init__.py | 3 + .../mermaid/sphinx_mermaid_extension.py | 245 ++++++++++++++++++ 6 files changed, 455 insertions(+) create mode 100644 _doc/api/mermaid.rst create mode 100644 _unittests/ut_mermaid/test_mermaid_extension.py create mode 100644 sphinx_runpython/mermaid/__init__.py create mode 100644 sphinx_runpython/mermaid/sphinx_mermaid_extension.py diff --git a/_doc/api/index.rst b/_doc/api/index.rst index c386747..92ed542 100644 --- a/_doc/api/index.rst +++ b/_doc/api/index.rst @@ -15,6 +15,7 @@ Extensions docassert epkg gdot + mermaid quote runpython tools diff --git a/_doc/api/mermaid.rst b/_doc/api/mermaid.rst new file mode 100644 index 0000000..98fa1fb --- /dev/null +++ b/_doc/api/mermaid.rst @@ -0,0 +1,77 @@ +======= +mermaid +======= + +This directive displays `Mermaid `_ diagrams in the documentation. +Diagrams are rendered client-side in *HTML* output via the Mermaid JavaScript library +and as verbatim text in *LaTeX* / *RST* output. + +Usage +===== + +In *conf.py*: + +:: + + extensions = [ ... + 'sphinx_runpython.mermaid', + ] + +One example: + +:: + + .. mermaid:: + + graph LR + A --> B --> C + +Which gives: + +.. mermaid:: + + graph LR + A --> B --> C + +The diagram source can also be produced by a Python script. +Option *script* must be specified: + +:: + + .. mermaid:: + :script: + + print(""" + graph LR + A --> B + """) + +.. mermaid:: + :script: + + print(""" + graph LR + A --> B + """) + +When *script* is a non-empty string it is used as a split token: only +the output **after** the first occurrence of that string is interpreted +as Mermaid source: + +:: + + .. mermaid:: + :script: AFTER-THIS + + print("preamble") + print("AFTER-THIS") + print("graph TD") + print(" P --> Q") + +Finally, the option ``:process:`` can be used to run the script in +a separate process. + +Directive +========= + +.. autoclass:: sphinx_runpython.mermaid.sphinx_mermaid_extension.MermaidDirective diff --git a/_doc/conf.py b/_doc/conf.py index 22c4aec..b87a8b3 100644 --- a/_doc/conf.py +++ b/_doc/conf.py @@ -25,6 +25,7 @@ "sphinx_runpython.docassert", "sphinx_runpython.gdot", "sphinx_runpython.epkg", + "sphinx_runpython.mermaid", "sphinx_runpython.quote", "sphinx_runpython.runpython", "sphinx_runpython.sphinx_rst_builder", @@ -118,6 +119,7 @@ "git": "https://git-scm.com/", "Graphviz": "https://graphviz.org/", "HTML": "https://simple.wikipedia.org/wiki/HTML", + "Mermaid": "https://mermaid.js.org/", "nested_parse_with_titles": "https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest", "numpy": ( "https://www.numpy.org/", diff --git a/_unittests/ut_mermaid/test_mermaid_extension.py b/_unittests/ut_mermaid/test_mermaid_extension.py new file mode 100644 index 0000000..edcded2 --- /dev/null +++ b/_unittests/ut_mermaid/test_mermaid_extension.py @@ -0,0 +1,127 @@ +import unittest +import logging +from sphinx_runpython.process_rst import rst2html +from sphinx_runpython.ext_test_case import ( + ExtTestCase, + ignore_warnings, +) + + +class TestMermaidExtension(ExtTestCase): + def setUp(self): + logger = logging.getLogger("mermaid") + logger.disabled = True + + @ignore_warnings(PendingDeprecationWarning) + def test_mermaid_inline_rst(self): + """Inline mermaid diagram is round-tripped through the RST writer.""" + content = """ +before + +.. mermaid:: + + graph LR + A --> B --> C + +after +""" + content = rst2html( + content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"] + ) + self.assertIn("graph LR", content) + self.assertIn("A --> B --> C", content) + + @ignore_warnings(PendingDeprecationWarning) + def test_mermaid_inline_html(self): + """Inline mermaid diagram produces a
 element."""
+        content = """
+before
+
+.. mermaid::
+
+    graph LR
+        A --> B
+
+after
+"""
+        html = rst2html(
+            content, writer_name="html", new_extensions=["sphinx_runpython.mermaid"]
+        )
+        self.assertIn('class="mermaid"', html)
+        self.assertIn("graph LR", html)
+        self.assertIn("A --> B", html)
+
+    @ignore_warnings(PendingDeprecationWarning)
+    def test_mermaid_script(self):
+        """Script-generated mermaid diagram is included in the RST output."""
+        content = """
+before
+
+.. mermaid::
+    :script:
+
+    print(\"\"\"graph LR
+    X --> Y\"\"\")
+
+after
+"""
+        content = rst2html(
+            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"]
+        )
+        self.assertIn("graph LR", content)
+        self.assertIn("X --> Y", content)
+
+    @ignore_warnings(PendingDeprecationWarning)
+    def test_mermaid_script_split(self):
+        """When :script: has a value it is used as a split token."""
+        content = """
+before
+
+.. mermaid::
+    :script: BEGIN
+
+    print("preamble")
+    print("BEGIN")
+    print("graph TD")
+    print("    P --> Q")
+
+after
+"""
+        content = rst2html(
+            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"]
+        )
+        self.assertNotIn("preamble", content)
+        self.assertNotIn("BEGIN", content)
+        self.assertIn("graph TD", content)
+        self.assertIn("P --> Q", content)
+
+    @ignore_warnings(PendingDeprecationWarning)
+    def test_mermaid_script_cache(self):
+        """Identical scripts produce the same output and are cached."""
+        script_body = 'print("graph LR\\n    A --> B")'
+        content = f"""
+before
+
+.. mermaid::
+    :script:
+
+    {script_body}
+
+middle
+
+.. mermaid::
+    :script:
+
+    {script_body}
+
+after
+"""
+        content = rst2html(
+            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"]
+        )
+        count = content.count("graph LR")
+        self.assertEqual(count, 2, f"Expected diagram code twice, got {count}")
+
+
+if __name__ == "__main__":
+    unittest.main(verbosity=2)
diff --git a/sphinx_runpython/mermaid/__init__.py b/sphinx_runpython/mermaid/__init__.py
new file mode 100644
index 0000000..d5cc656
--- /dev/null
+++ b/sphinx_runpython/mermaid/__init__.py
@@ -0,0 +1,3 @@
+from .sphinx_mermaid_extension import setup
+
+__all__ = ["setup"]
diff --git a/sphinx_runpython/mermaid/sphinx_mermaid_extension.py b/sphinx_runpython/mermaid/sphinx_mermaid_extension.py
new file mode 100644
index 0000000..721c739
--- /dev/null
+++ b/sphinx_runpython/mermaid/sphinx_mermaid_extension.py
@@ -0,0 +1,245 @@
+import hashlib
+import logging
+from docutils import nodes
+from docutils.parsers.rst import directives, Directive
+import sphinx
+from ..ext_helper import get_env_state_info
+from ..runpython.sphinx_runpython_extension import run_python_script
+
+logger = logging.getLogger("mermaid")
+
+#: Default CDN URL for the mermaid JavaScript library.
+_MERMAID_JS_URL = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"
+
+
+class mermaid_node(nodes.General, nodes.Element):
+    """
+    Defines ``mermaid`` node.
+    """
+
+    pass
+
+
+class MermaidDirective(Directive):
+    """
+    A ``mermaid`` node displays a `Mermaid `_ diagram.
+
+    For *HTML* output the diagram is rendered client-side by embedding the
+    Mermaid JavaScript library (loaded from a CDN or a local copy).
+    For *LaTeX* / *text* / *RST* output the raw Mermaid source is included.
+
+    Supported options:
+
+    * *script*: boolean or a string that marks the beginning of the Mermaid
+      source in the standard output of the embedded Python script.  When this
+      option is present the directive body is interpreted as Python code whose
+      ``stdout`` contains the diagram definition.
+    * *process*: run the Python script in a separate process.
+
+    Example – inline diagram::
+
+        .. mermaid::
+
+            graph LR
+                A --> B --> C
+
+    Which gives:
+
+    .. mermaid::
+
+        graph LR
+            A --> B --> C
+
+    Example – script-generated diagram::
+
+        .. mermaid::
+            :script:
+
+            print(\"\"\"
+            graph LR
+                A --> B
+            \"\"\")
+
+    .. mermaid::
+        :script:
+
+        print(\"\"\"
+        graph LR
+            A --> B
+        \"\"\")
+    """
+
+    node_class = mermaid_node
+    has_content = True
+    required_arguments = 0
+    optional_arguments = 0
+    final_argument_whitespace = False
+    option_spec = {
+        "script": directives.unchanged,
+        "process": directives.unchanged,
+    }
+
+    def run(self):
+        """Build the mermaid node."""
+        bool_set_ = (True, 1, "True", "1", "true", "")
+        process = "process" in self.options and self.options["process"] in bool_set_
+
+        info = get_env_state_info(self)
+        docname = info["docname"]
+
+        if "script" in self.options:
+            script = self.options["script"]
+            if script in (0, "0", "False", "false"):
+                script = None
+            elif script in (1, "1", "True", "true", ""):
+                script = ""
+            elif len(script) == 0:
+                raise RuntimeError(
+                    "script should be a string to indicate"
+                    " the beginning of the Mermaid diagram."
+                )
+        else:
+            script = False
+
+        # Execute the script and use its stdout as diagram source, if requested.
+        content = "\n".join(self.content)
+        if script or script == "":
+            env = info.get("env")
+            doc_prefix = docname.split("/")[-1] if docname else ""
+            cache_key = (
+                f"{doc_prefix}:"
+                + hashlib.sha256(f"{content}:{process}".encode()).hexdigest()
+            )
+            if env is not None:
+                if not hasattr(env, "mermaid_script_cache"):
+                    env.mermaid_script_cache = {}
+                cached = env.mermaid_script_cache.get(cache_key, None)
+            else:
+                cached = None
+
+            if cached is not None:
+                stdout, stderr = cached
+            else:
+                stdout, stderr, _ = run_python_script(content, process=process)
+                if env is not None:
+                    env.mermaid_script_cache[cache_key] = (stdout, stderr)
+
+            if stderr:
+                logger.warning(
+                    "[mermaid] a diagram cannot be drawn due to %s", stderr
+                )
+            content = stdout
+            if script:
+                spl = content.split(script)
+                if len(spl) > 2:
+                    logger.warning("[mermaid] too many output lines %s", content)
+                content = spl[-1]
+
+        node = mermaid_node(code=content, options={"docname": docname})
+        return [node]
+
+
+# ---------------------------------------------------------------------------
+# Visitor helpers
+# ---------------------------------------------------------------------------
+
+
+def visit_mermaid_node_html(self, node):
+    """Render the mermaid node in HTML output."""
+    code = node["code"].strip()
+    # Emit a 
 block; mermaid.js will replace it at runtime.
+    self.body.append(
+        f'
' + f'
{self.encode(code)}
' + f"
\n" + ) + raise nodes.SkipNode + + +def depart_mermaid_node_html(self, node): + """depart – not called because visit raises SkipNode.""" + + +def visit_mermaid_node_rst(self, node): + """Render the mermaid node in RST output.""" + self.new_state(0) + self.add_text(".. mermaid::" + self.nl) + self.new_state(self.indent) + for row in node["code"].split("\n"): + self.add_text(row + self.nl) + + +def depart_mermaid_node_rst(self, node): + """depart mermaid node in RST output.""" + self.end_state() + self.end_state(wrap=False) + + +def visit_mermaid_node_text(self, node): + """Render the mermaid node in plain-text output.""" + self.new_state(0) + self.add_text("[mermaid diagram]\n") + self.new_state(self.indent) + for row in node["code"].split("\n"): + self.add_text(row + self.nl) + + +def depart_mermaid_node_text(self, node): + """depart mermaid node in text output.""" + self.end_state() + self.end_state(wrap=False) + + +def visit_mermaid_node_latex(self, node): + """Render the mermaid node in LaTeX output (verbatim source).""" + code = node["code"].strip() + self.body.append("\n\\begin{verbatim}\n") + self.body.append(code) + self.body.append("\n\\end{verbatim}\n") + raise nodes.SkipNode + + +def depart_mermaid_node_latex(self, node): + """depart – not called because visit raises SkipNode.""" + + +# --------------------------------------------------------------------------- +# JS injection +# --------------------------------------------------------------------------- + + +def add_mermaid_js(app): + """Inject the Mermaid JS library into HTML pages.""" + if app.builder.format != "html": + return + app.add_js_file(_MERMAID_JS_URL, **{"loading_method": "async"}) + # Initialise mermaid after the DOM is ready. + app.add_js_file( + None, + body="document.addEventListener('DOMContentLoaded', function() { mermaid.initialize({startOnLoad: true}); });", + ) + + +# --------------------------------------------------------------------------- +# Extension setup +# --------------------------------------------------------------------------- + + +def setup(app): + """ + setup for ``mermaid`` (sphinx) + """ + app.connect("builder-inited", add_mermaid_js) + + app.add_node( + mermaid_node, + html=(visit_mermaid_node_html, depart_mermaid_node_html), + epub=(visit_mermaid_node_html, depart_mermaid_node_html), + latex=(visit_mermaid_node_latex, depart_mermaid_node_latex), + text=(visit_mermaid_node_text, depart_mermaid_node_text), + rst=(visit_mermaid_node_rst, depart_mermaid_node_rst), + md=(visit_mermaid_node_text, depart_mermaid_node_text), + ) + + app.add_directive("mermaid", MermaidDirective) + return {"version": sphinx.__display_version__, "parallel_read_safe": True} From a58837a89b04c5cbc9c7b55bb1efc173410b9724 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 09:25:41 +0000 Subject: [PATCH 3/7] fix unreachable branch and improve docstrings in mermaid extension Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/d8720735-e1ac-42f5-8f74-5cb189420a5e Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> --- sphinx_runpython/mermaid/sphinx_mermaid_extension.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sphinx_runpython/mermaid/sphinx_mermaid_extension.py b/sphinx_runpython/mermaid/sphinx_mermaid_extension.py index 721c739..9a9af03 100644 --- a/sphinx_runpython/mermaid/sphinx_mermaid_extension.py +++ b/sphinx_runpython/mermaid/sphinx_mermaid_extension.py @@ -93,11 +93,7 @@ def run(self): script = None elif script in (1, "1", "True", "true", ""): script = "" - elif len(script) == 0: - raise RuntimeError( - "script should be a string to indicate" - " the beginning of the Mermaid diagram." - ) + # else: keep script as-is to use it as a split token else: script = False @@ -157,7 +153,7 @@ def visit_mermaid_node_html(self, node): def depart_mermaid_node_html(self, node): - """depart – not called because visit raises SkipNode.""" + """Depart the mermaid HTML node. Not called because the visitor raises SkipNode.""" def visit_mermaid_node_rst(self, node): @@ -200,7 +196,7 @@ def visit_mermaid_node_latex(self, node): def depart_mermaid_node_latex(self, node): - """depart – not called because visit raises SkipNode.""" + """Depart the mermaid LaTeX node. Not called because the visitor raises SkipNode.""" # --------------------------------------------------------------------------- From 975cd5561d01a962eab1d6f7b8256a33bc103123 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 4 May 2026 09:34:05 +0000 Subject: [PATCH 4/7] rename mermaid to mermaidjs to avoid conflicts with existing packages Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/0b9e316d-85dd-491f-a8fe-fcb57e2c1479 Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com> --- _doc/api/index.rst | 2 +- _doc/api/{mermaid.rst => mermaidjs.rst} | 20 ++--- _doc/conf.py | 2 +- .../test_mermaidjs_extension.py} | 42 ++++----- sphinx_runpython/mermaid/__init__.py | 3 - sphinx_runpython/mermaidjs/__init__.py | 3 + .../sphinx_mermaidjs_extension.py} | 88 +++++++++---------- 7 files changed, 80 insertions(+), 80 deletions(-) rename _doc/api/{mermaid.rst => mermaidjs.rst} (81%) rename _unittests/{ut_mermaid/test_mermaid_extension.py => ut_mermaidjs/test_mermaidjs_extension.py} (76%) delete mode 100644 sphinx_runpython/mermaid/__init__.py create mode 100644 sphinx_runpython/mermaidjs/__init__.py rename sphinx_runpython/{mermaid/sphinx_mermaid_extension.py => mermaidjs/sphinx_mermaidjs_extension.py} (68%) diff --git a/_doc/api/index.rst b/_doc/api/index.rst index 92ed542..35d8aa1 100644 --- a/_doc/api/index.rst +++ b/_doc/api/index.rst @@ -15,7 +15,7 @@ Extensions docassert epkg gdot - mermaid + mermaidjs quote runpython tools diff --git a/_doc/api/mermaid.rst b/_doc/api/mermaidjs.rst similarity index 81% rename from _doc/api/mermaid.rst rename to _doc/api/mermaidjs.rst index 98fa1fb..c6d5d79 100644 --- a/_doc/api/mermaid.rst +++ b/_doc/api/mermaidjs.rst @@ -1,6 +1,6 @@ -======= -mermaid -======= +========= +mermaidjs +========= This directive displays `Mermaid `_ diagrams in the documentation. Diagrams are rendered client-side in *HTML* output via the Mermaid JavaScript library @@ -14,21 +14,21 @@ In *conf.py*: :: extensions = [ ... - 'sphinx_runpython.mermaid', + 'sphinx_runpython.mermaidjs', ] One example: :: - .. mermaid:: + .. mermaidjs:: graph LR A --> B --> C Which gives: -.. mermaid:: +.. mermaidjs:: graph LR A --> B --> C @@ -38,7 +38,7 @@ Option *script* must be specified: :: - .. mermaid:: + .. mermaidjs:: :script: print(""" @@ -46,7 +46,7 @@ Option *script* must be specified: A --> B """) -.. mermaid:: +.. mermaidjs:: :script: print(""" @@ -60,7 +60,7 @@ as Mermaid source: :: - .. mermaid:: + .. mermaidjs:: :script: AFTER-THIS print("preamble") @@ -74,4 +74,4 @@ a separate process. Directive ========= -.. autoclass:: sphinx_runpython.mermaid.sphinx_mermaid_extension.MermaidDirective +.. autoclass:: sphinx_runpython.mermaidjs.sphinx_mermaidjs_extension.MermaidDirective diff --git a/_doc/conf.py b/_doc/conf.py index b87a8b3..53e13f6 100644 --- a/_doc/conf.py +++ b/_doc/conf.py @@ -25,7 +25,7 @@ "sphinx_runpython.docassert", "sphinx_runpython.gdot", "sphinx_runpython.epkg", - "sphinx_runpython.mermaid", + "sphinx_runpython.mermaidjs", "sphinx_runpython.quote", "sphinx_runpython.runpython", "sphinx_runpython.sphinx_rst_builder", diff --git a/_unittests/ut_mermaid/test_mermaid_extension.py b/_unittests/ut_mermaidjs/test_mermaidjs_extension.py similarity index 76% rename from _unittests/ut_mermaid/test_mermaid_extension.py rename to _unittests/ut_mermaidjs/test_mermaidjs_extension.py index edcded2..9610d9a 100644 --- a/_unittests/ut_mermaid/test_mermaid_extension.py +++ b/_unittests/ut_mermaidjs/test_mermaidjs_extension.py @@ -7,18 +7,18 @@ ) -class TestMermaidExtension(ExtTestCase): +class TestMermaidJsExtension(ExtTestCase): def setUp(self): - logger = logging.getLogger("mermaid") + logger = logging.getLogger("mermaidjs") logger.disabled = True @ignore_warnings(PendingDeprecationWarning) - def test_mermaid_inline_rst(self): - """Inline mermaid diagram is round-tripped through the RST writer.""" + def test_mermaidjs_inline_rst(self): + """Inline mermaidjs diagram is round-tripped through the RST writer.""" content = """ before -.. mermaid:: +.. mermaidjs:: graph LR A --> B --> C @@ -26,18 +26,18 @@ def test_mermaid_inline_rst(self): after """ content = rst2html( - content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"] + content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"] ) self.assertIn("graph LR", content) self.assertIn("A --> B --> C", content) @ignore_warnings(PendingDeprecationWarning) - def test_mermaid_inline_html(self): - """Inline mermaid diagram produces a
 element."""
+    def test_mermaidjs_inline_html(self):
+        """Inline mermaidjs diagram produces a 
 element."""
         content = """
 before
 
-.. mermaid::
+.. mermaidjs::
 
     graph LR
         A --> B
@@ -45,19 +45,19 @@ def test_mermaid_inline_html(self):
 after
 """
         html = rst2html(
-            content, writer_name="html", new_extensions=["sphinx_runpython.mermaid"]
+            content, writer_name="html", new_extensions=["sphinx_runpython.mermaidjs"]
         )
         self.assertIn('class="mermaid"', html)
         self.assertIn("graph LR", html)
         self.assertIn("A --> B", html)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaid_script(self):
-        """Script-generated mermaid diagram is included in the RST output."""
+    def test_mermaidjs_script(self):
+        """Script-generated mermaidjs diagram is included in the RST output."""
         content = """
 before
 
-.. mermaid::
+.. mermaidjs::
     :script:
 
     print(\"\"\"graph LR
@@ -66,18 +66,18 @@ def test_mermaid_script(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
         )
         self.assertIn("graph LR", content)
         self.assertIn("X --> Y", content)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaid_script_split(self):
+    def test_mermaidjs_script_split(self):
         """When :script: has a value it is used as a split token."""
         content = """
 before
 
-.. mermaid::
+.. mermaidjs::
     :script: BEGIN
 
     print("preamble")
@@ -88,7 +88,7 @@ def test_mermaid_script_split(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
         )
         self.assertNotIn("preamble", content)
         self.assertNotIn("BEGIN", content)
@@ -96,20 +96,20 @@ def test_mermaid_script_split(self):
         self.assertIn("P --> Q", content)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaid_script_cache(self):
+    def test_mermaidjs_script_cache(self):
         """Identical scripts produce the same output and are cached."""
         script_body = 'print("graph LR\\n    A --> B")'
         content = f"""
 before
 
-.. mermaid::
+.. mermaidjs::
     :script:
 
     {script_body}
 
 middle
 
-.. mermaid::
+.. mermaidjs::
     :script:
 
     {script_body}
@@ -117,7 +117,7 @@ def test_mermaid_script_cache(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaid"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
         )
         count = content.count("graph LR")
         self.assertEqual(count, 2, f"Expected diagram code twice, got {count}")
diff --git a/sphinx_runpython/mermaid/__init__.py b/sphinx_runpython/mermaid/__init__.py
deleted file mode 100644
index d5cc656..0000000
--- a/sphinx_runpython/mermaid/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .sphinx_mermaid_extension import setup
-
-__all__ = ["setup"]
diff --git a/sphinx_runpython/mermaidjs/__init__.py b/sphinx_runpython/mermaidjs/__init__.py
new file mode 100644
index 0000000..b843779
--- /dev/null
+++ b/sphinx_runpython/mermaidjs/__init__.py
@@ -0,0 +1,3 @@
+from .sphinx_mermaidjs_extension import setup
+
+__all__ = ["setup"]
diff --git a/sphinx_runpython/mermaid/sphinx_mermaid_extension.py b/sphinx_runpython/mermaidjs/sphinx_mermaidjs_extension.py
similarity index 68%
rename from sphinx_runpython/mermaid/sphinx_mermaid_extension.py
rename to sphinx_runpython/mermaidjs/sphinx_mermaidjs_extension.py
index 9a9af03..44dfe2b 100644
--- a/sphinx_runpython/mermaid/sphinx_mermaid_extension.py
+++ b/sphinx_runpython/mermaidjs/sphinx_mermaidjs_extension.py
@@ -6,15 +6,15 @@
 from ..ext_helper import get_env_state_info
 from ..runpython.sphinx_runpython_extension import run_python_script
 
-logger = logging.getLogger("mermaid")
+logger = logging.getLogger("mermaidjs")
 
 #: Default CDN URL for the mermaid JavaScript library.
 _MERMAID_JS_URL = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"
 
 
-class mermaid_node(nodes.General, nodes.Element):
+class mermaidjs_node(nodes.General, nodes.Element):
     """
-    Defines ``mermaid`` node.
+    Defines ``mermaidjs`` node.
     """
 
     pass
@@ -22,7 +22,7 @@ class mermaid_node(nodes.General, nodes.Element):
 
 class MermaidDirective(Directive):
     """
-    A ``mermaid`` node displays a `Mermaid `_ diagram.
+    A ``mermaidjs`` node displays a `Mermaid `_ diagram.
 
     For *HTML* output the diagram is rendered client-side by embedding the
     Mermaid JavaScript library (loaded from a CDN or a local copy).
@@ -38,21 +38,21 @@ class MermaidDirective(Directive):
 
     Example – inline diagram::
 
-        .. mermaid::
+        .. mermaidjs::
 
             graph LR
                 A --> B --> C
 
     Which gives:
 
-    .. mermaid::
+    .. mermaidjs::
 
         graph LR
             A --> B --> C
 
     Example – script-generated diagram::
 
-        .. mermaid::
+        .. mermaidjs::
             :script:
 
             print(\"\"\"
@@ -60,7 +60,7 @@ class MermaidDirective(Directive):
                 A --> B
             \"\"\")
 
-    .. mermaid::
+    .. mermaidjs::
         :script:
 
         print(\"\"\"
@@ -69,7 +69,7 @@ class MermaidDirective(Directive):
         \"\"\")
     """
 
-    node_class = mermaid_node
+    node_class = mermaidjs_node
     has_content = True
     required_arguments = 0
     optional_arguments = 0
@@ -80,7 +80,7 @@ class MermaidDirective(Directive):
     }
 
     def run(self):
-        """Build the mermaid node."""
+        """Build the mermaidjs node."""
         bool_set_ = (True, 1, "True", "1", "true", "")
         process = "process" in self.options and self.options["process"] in bool_set_
 
@@ -107,9 +107,9 @@ def run(self):
                 + hashlib.sha256(f"{content}:{process}".encode()).hexdigest()
             )
             if env is not None:
-                if not hasattr(env, "mermaid_script_cache"):
-                    env.mermaid_script_cache = {}
-                cached = env.mermaid_script_cache.get(cache_key, None)
+                if not hasattr(env, "mermaidjs_script_cache"):
+                    env.mermaidjs_script_cache = {}
+                cached = env.mermaidjs_script_cache.get(cache_key, None)
             else:
                 cached = None
 
@@ -118,20 +118,20 @@ def run(self):
             else:
                 stdout, stderr, _ = run_python_script(content, process=process)
                 if env is not None:
-                    env.mermaid_script_cache[cache_key] = (stdout, stderr)
+                    env.mermaidjs_script_cache[cache_key] = (stdout, stderr)
 
             if stderr:
                 logger.warning(
-                    "[mermaid] a diagram cannot be drawn due to %s", stderr
+                    "[mermaidjs] a diagram cannot be drawn due to %s", stderr
                 )
             content = stdout
             if script:
                 spl = content.split(script)
                 if len(spl) > 2:
-                    logger.warning("[mermaid] too many output lines %s", content)
+                    logger.warning("[mermaidjs] too many output lines %s", content)
                 content = spl[-1]
 
-        node = mermaid_node(code=content, options={"docname": docname})
+        node = mermaidjs_node(code=content, options={"docname": docname})
         return [node]
 
 
@@ -140,8 +140,8 @@ def run(self):
 # ---------------------------------------------------------------------------
 
 
-def visit_mermaid_node_html(self, node):
-    """Render the mermaid node in HTML output."""
+def visit_mermaidjs_node_html(self, node):
+    """Render the mermaidjs node in HTML output."""
     code = node["code"].strip()
     # Emit a 
 block; mermaid.js will replace it at runtime.
     self.body.append(
@@ -152,42 +152,42 @@ def visit_mermaid_node_html(self, node):
     raise nodes.SkipNode
 
 
-def depart_mermaid_node_html(self, node):
-    """Depart the mermaid HTML node. Not called because the visitor raises SkipNode."""
+def depart_mermaidjs_node_html(self, node):
+    """Depart the mermaidjs HTML node. Not called because the visitor raises SkipNode."""
 
 
-def visit_mermaid_node_rst(self, node):
-    """Render the mermaid node in RST output."""
+def visit_mermaidjs_node_rst(self, node):
+    """Render the mermaidjs node in RST output."""
     self.new_state(0)
-    self.add_text(".. mermaid::" + self.nl)
+    self.add_text(".. mermaidjs::" + self.nl)
     self.new_state(self.indent)
     for row in node["code"].split("\n"):
         self.add_text(row + self.nl)
 
 
-def depart_mermaid_node_rst(self, node):
-    """depart mermaid node in RST output."""
+def depart_mermaidjs_node_rst(self, node):
+    """Depart mermaidjs node in RST output."""
     self.end_state()
     self.end_state(wrap=False)
 
 
-def visit_mermaid_node_text(self, node):
-    """Render the mermaid node in plain-text output."""
+def visit_mermaidjs_node_text(self, node):
+    """Render the mermaidjs node in plain-text output."""
     self.new_state(0)
-    self.add_text("[mermaid diagram]\n")
+    self.add_text("[mermaidjs diagram]\n")
     self.new_state(self.indent)
     for row in node["code"].split("\n"):
         self.add_text(row + self.nl)
 
 
-def depart_mermaid_node_text(self, node):
-    """depart mermaid node in text output."""
+def depart_mermaidjs_node_text(self, node):
+    """Depart mermaidjs node in text output."""
     self.end_state()
     self.end_state(wrap=False)
 
 
-def visit_mermaid_node_latex(self, node):
-    """Render the mermaid node in LaTeX output (verbatim source)."""
+def visit_mermaidjs_node_latex(self, node):
+    """Render the mermaidjs node in LaTeX output (verbatim source)."""
     code = node["code"].strip()
     self.body.append("\n\\begin{verbatim}\n")
     self.body.append(code)
@@ -195,8 +195,8 @@ def visit_mermaid_node_latex(self, node):
     raise nodes.SkipNode
 
 
-def depart_mermaid_node_latex(self, node):
-    """Depart the mermaid LaTeX node. Not called because the visitor raises SkipNode."""
+def depart_mermaidjs_node_latex(self, node):
+    """Depart the mermaidjs LaTeX node. Not called because the visitor raises SkipNode."""
 
 
 # ---------------------------------------------------------------------------
@@ -223,19 +223,19 @@ def add_mermaid_js(app):
 
 def setup(app):
     """
-    setup for ``mermaid`` (sphinx)
+    setup for ``mermaidjs`` (sphinx)
     """
     app.connect("builder-inited", add_mermaid_js)
 
     app.add_node(
-        mermaid_node,
-        html=(visit_mermaid_node_html, depart_mermaid_node_html),
-        epub=(visit_mermaid_node_html, depart_mermaid_node_html),
-        latex=(visit_mermaid_node_latex, depart_mermaid_node_latex),
-        text=(visit_mermaid_node_text, depart_mermaid_node_text),
-        rst=(visit_mermaid_node_rst, depart_mermaid_node_rst),
-        md=(visit_mermaid_node_text, depart_mermaid_node_text),
+        mermaidjs_node,
+        html=(visit_mermaidjs_node_html, depart_mermaidjs_node_html),
+        epub=(visit_mermaidjs_node_html, depart_mermaidjs_node_html),
+        latex=(visit_mermaidjs_node_latex, depart_mermaidjs_node_latex),
+        text=(visit_mermaidjs_node_text, depart_mermaidjs_node_text),
+        rst=(visit_mermaidjs_node_rst, depart_mermaidjs_node_rst),
+        md=(visit_mermaidjs_node_text, depart_mermaidjs_node_text),
     )
 
-    app.add_directive("mermaid", MermaidDirective)
+    app.add_directive("mermaidjs", MermaidDirective)
     return {"version": sphinx.__display_version__, "parallel_read_safe": True}

From 0a7c4b813bf48922ffd14159328966a2cb3e3979 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 4 May 2026 09:46:03 +0000
Subject: [PATCH 5/7] rename mermaidjs to runmermaid

Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/d779d538-fb95-4f79-ba03-95dfc33cf6e0

Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com>
---
 _doc/api/index.rst                            |  2 +-
 _doc/api/{mermaidjs.rst => runmermaid.rst}    | 20 ++---
 _doc/conf.py                                  |  2 +-
 .../test_runmermaid_extension.py}             | 42 ++++-----
 sphinx_runpython/mermaidjs/__init__.py        |  3 -
 sphinx_runpython/runmermaid/__init__.py       |  3 +
 .../sphinx_runmermaid_extension.py}           | 90 +++++++++----------
 7 files changed, 81 insertions(+), 81 deletions(-)
 rename _doc/api/{mermaidjs.rst => runmermaid.rst} (80%)
 rename _unittests/{ut_mermaidjs/test_mermaidjs_extension.py => ut_runmermaid/test_runmermaid_extension.py} (75%)
 delete mode 100644 sphinx_runpython/mermaidjs/__init__.py
 create mode 100644 sphinx_runpython/runmermaid/__init__.py
 rename sphinx_runpython/{mermaidjs/sphinx_mermaidjs_extension.py => runmermaid/sphinx_runmermaid_extension.py} (68%)

diff --git a/_doc/api/index.rst b/_doc/api/index.rst
index 35d8aa1..f382c62 100644
--- a/_doc/api/index.rst
+++ b/_doc/api/index.rst
@@ -15,7 +15,7 @@ Extensions
     docassert
     epkg
     gdot
-    mermaidjs
+    runmermaid
     quote
     runpython
     tools
diff --git a/_doc/api/mermaidjs.rst b/_doc/api/runmermaid.rst
similarity index 80%
rename from _doc/api/mermaidjs.rst
rename to _doc/api/runmermaid.rst
index c6d5d79..678fa4b 100644
--- a/_doc/api/mermaidjs.rst
+++ b/_doc/api/runmermaid.rst
@@ -1,6 +1,6 @@
-=========
-mermaidjs
-=========
+==========
+runmermaid
+==========
 
 This directive displays `Mermaid `_ diagrams in the documentation.
 Diagrams are rendered client-side in *HTML* output via the Mermaid JavaScript library
@@ -14,21 +14,21 @@ In *conf.py*:
 ::
 
     extensions = [ ...
-        'sphinx_runpython.mermaidjs',
+        'sphinx_runpython.runmermaid',
     ]
 
 One example:
 
 ::
 
-    .. mermaidjs::
+    .. runmermaid::
 
         graph LR
             A --> B --> C
 
 Which gives:
 
-.. mermaidjs::
+.. runmermaid::
 
     graph LR
         A --> B --> C
@@ -38,7 +38,7 @@ Option *script* must be specified:
 
 ::
 
-    .. mermaidjs::
+    .. runmermaid::
         :script:
 
         print("""
@@ -46,7 +46,7 @@ Option *script* must be specified:
             A --> B
         """)
 
-.. mermaidjs::
+.. runmermaid::
     :script:
 
     print("""
@@ -60,7 +60,7 @@ as Mermaid source:
 
 ::
 
-    .. mermaidjs::
+    .. runmermaid::
         :script: AFTER-THIS
 
         print("preamble")
@@ -74,4 +74,4 @@ a separate process.
 Directive
 =========
 
-.. autoclass:: sphinx_runpython.mermaidjs.sphinx_mermaidjs_extension.MermaidDirective
+.. autoclass:: sphinx_runpython.runmermaid.sphinx_runmermaid_extension.RunMermaidDirective
diff --git a/_doc/conf.py b/_doc/conf.py
index 53e13f6..81756aa 100644
--- a/_doc/conf.py
+++ b/_doc/conf.py
@@ -25,7 +25,7 @@
     "sphinx_runpython.docassert",
     "sphinx_runpython.gdot",
     "sphinx_runpython.epkg",
-    "sphinx_runpython.mermaidjs",
+    "sphinx_runpython.runmermaid",
     "sphinx_runpython.quote",
     "sphinx_runpython.runpython",
     "sphinx_runpython.sphinx_rst_builder",
diff --git a/_unittests/ut_mermaidjs/test_mermaidjs_extension.py b/_unittests/ut_runmermaid/test_runmermaid_extension.py
similarity index 75%
rename from _unittests/ut_mermaidjs/test_mermaidjs_extension.py
rename to _unittests/ut_runmermaid/test_runmermaid_extension.py
index 9610d9a..d6fa250 100644
--- a/_unittests/ut_mermaidjs/test_mermaidjs_extension.py
+++ b/_unittests/ut_runmermaid/test_runmermaid_extension.py
@@ -7,18 +7,18 @@
 )
 
 
-class TestMermaidJsExtension(ExtTestCase):
+class TestRunMermaidExtension(ExtTestCase):
     def setUp(self):
-        logger = logging.getLogger("mermaidjs")
+        logger = logging.getLogger("runmermaid")
         logger.disabled = True
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaidjs_inline_rst(self):
-        """Inline mermaidjs diagram is round-tripped through the RST writer."""
+    def test_runmermaid_inline_rst(self):
+        """Inline runmermaid diagram is round-tripped through the RST writer."""
         content = """
 before
 
-.. mermaidjs::
+.. runmermaid::
 
     graph LR
         A --> B --> C
@@ -26,18 +26,18 @@ def test_mermaidjs_inline_rst(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.runmermaid"]
         )
         self.assertIn("graph LR", content)
         self.assertIn("A --> B --> C", content)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaidjs_inline_html(self):
-        """Inline mermaidjs diagram produces a 
 element."""
+    def test_runmermaid_inline_html(self):
+        """Inline runmermaid diagram produces a 
 element."""
         content = """
 before
 
-.. mermaidjs::
+.. runmermaid::
 
     graph LR
         A --> B
@@ -45,19 +45,19 @@ def test_mermaidjs_inline_html(self):
 after
 """
         html = rst2html(
-            content, writer_name="html", new_extensions=["sphinx_runpython.mermaidjs"]
+            content, writer_name="html", new_extensions=["sphinx_runpython.runmermaid"]
         )
         self.assertIn('class="mermaid"', html)
         self.assertIn("graph LR", html)
         self.assertIn("A --> B", html)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaidjs_script(self):
-        """Script-generated mermaidjs diagram is included in the RST output."""
+    def test_runmermaid_script(self):
+        """Script-generated runmermaid diagram is included in the RST output."""
         content = """
 before
 
-.. mermaidjs::
+.. runmermaid::
     :script:
 
     print(\"\"\"graph LR
@@ -66,18 +66,18 @@ def test_mermaidjs_script(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.runmermaid"]
         )
         self.assertIn("graph LR", content)
         self.assertIn("X --> Y", content)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaidjs_script_split(self):
+    def test_runmermaid_script_split(self):
         """When :script: has a value it is used as a split token."""
         content = """
 before
 
-.. mermaidjs::
+.. runmermaid::
     :script: BEGIN
 
     print("preamble")
@@ -88,7 +88,7 @@ def test_mermaidjs_script_split(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.runmermaid"]
         )
         self.assertNotIn("preamble", content)
         self.assertNotIn("BEGIN", content)
@@ -96,20 +96,20 @@ def test_mermaidjs_script_split(self):
         self.assertIn("P --> Q", content)
 
     @ignore_warnings(PendingDeprecationWarning)
-    def test_mermaidjs_script_cache(self):
+    def test_runmermaid_script_cache(self):
         """Identical scripts produce the same output and are cached."""
         script_body = 'print("graph LR\\n    A --> B")'
         content = f"""
 before
 
-.. mermaidjs::
+.. runmermaid::
     :script:
 
     {script_body}
 
 middle
 
-.. mermaidjs::
+.. runmermaid::
     :script:
 
     {script_body}
@@ -117,7 +117,7 @@ def test_mermaidjs_script_cache(self):
 after
 """
         content = rst2html(
-            content, writer_name="rst", new_extensions=["sphinx_runpython.mermaidjs"]
+            content, writer_name="rst", new_extensions=["sphinx_runpython.runmermaid"]
         )
         count = content.count("graph LR")
         self.assertEqual(count, 2, f"Expected diagram code twice, got {count}")
diff --git a/sphinx_runpython/mermaidjs/__init__.py b/sphinx_runpython/mermaidjs/__init__.py
deleted file mode 100644
index b843779..0000000
--- a/sphinx_runpython/mermaidjs/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .sphinx_mermaidjs_extension import setup
-
-__all__ = ["setup"]
diff --git a/sphinx_runpython/runmermaid/__init__.py b/sphinx_runpython/runmermaid/__init__.py
new file mode 100644
index 0000000..185687f
--- /dev/null
+++ b/sphinx_runpython/runmermaid/__init__.py
@@ -0,0 +1,3 @@
+from .sphinx_runmermaid_extension import setup
+
+__all__ = ["setup"]
diff --git a/sphinx_runpython/mermaidjs/sphinx_mermaidjs_extension.py b/sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py
similarity index 68%
rename from sphinx_runpython/mermaidjs/sphinx_mermaidjs_extension.py
rename to sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py
index 44dfe2b..87b806a 100644
--- a/sphinx_runpython/mermaidjs/sphinx_mermaidjs_extension.py
+++ b/sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py
@@ -6,23 +6,23 @@
 from ..ext_helper import get_env_state_info
 from ..runpython.sphinx_runpython_extension import run_python_script
 
-logger = logging.getLogger("mermaidjs")
+logger = logging.getLogger("runmermaid")
 
 #: Default CDN URL for the mermaid JavaScript library.
 _MERMAID_JS_URL = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"
 
 
-class mermaidjs_node(nodes.General, nodes.Element):
+class runmermaid_node(nodes.General, nodes.Element):
     """
-    Defines ``mermaidjs`` node.
+    Defines ``runmermaid`` node.
     """
 
     pass
 
 
-class MermaidDirective(Directive):
+class RunMermaidDirective(Directive):
     """
-    A ``mermaidjs`` node displays a `Mermaid `_ diagram.
+    A ``runmermaid`` node displays a `Mermaid `_ diagram.
 
     For *HTML* output the diagram is rendered client-side by embedding the
     Mermaid JavaScript library (loaded from a CDN or a local copy).
@@ -38,21 +38,21 @@ class MermaidDirective(Directive):
 
     Example – inline diagram::
 
-        .. mermaidjs::
+        .. runmermaid::
 
             graph LR
                 A --> B --> C
 
     Which gives:
 
-    .. mermaidjs::
+    .. runmermaid::
 
         graph LR
             A --> B --> C
 
     Example – script-generated diagram::
 
-        .. mermaidjs::
+        .. runmermaid::
             :script:
 
             print(\"\"\"
@@ -60,7 +60,7 @@ class MermaidDirective(Directive):
                 A --> B
             \"\"\")
 
-    .. mermaidjs::
+    .. runmermaid::
         :script:
 
         print(\"\"\"
@@ -69,7 +69,7 @@ class MermaidDirective(Directive):
         \"\"\")
     """
 
-    node_class = mermaidjs_node
+    node_class = runmermaid_node
     has_content = True
     required_arguments = 0
     optional_arguments = 0
@@ -80,7 +80,7 @@ class MermaidDirective(Directive):
     }
 
     def run(self):
-        """Build the mermaidjs node."""
+        """Build the runmermaid node."""
         bool_set_ = (True, 1, "True", "1", "true", "")
         process = "process" in self.options and self.options["process"] in bool_set_
 
@@ -107,9 +107,9 @@ def run(self):
                 + hashlib.sha256(f"{content}:{process}".encode()).hexdigest()
             )
             if env is not None:
-                if not hasattr(env, "mermaidjs_script_cache"):
-                    env.mermaidjs_script_cache = {}
-                cached = env.mermaidjs_script_cache.get(cache_key, None)
+                if not hasattr(env, "runmermaid_script_cache"):
+                    env.runmermaid_script_cache = {}
+                cached = env.runmermaid_script_cache.get(cache_key, None)
             else:
                 cached = None
 
@@ -118,20 +118,20 @@ def run(self):
             else:
                 stdout, stderr, _ = run_python_script(content, process=process)
                 if env is not None:
-                    env.mermaidjs_script_cache[cache_key] = (stdout, stderr)
+                    env.runmermaid_script_cache[cache_key] = (stdout, stderr)
 
             if stderr:
                 logger.warning(
-                    "[mermaidjs] a diagram cannot be drawn due to %s", stderr
+                    "[runmermaid] a diagram cannot be drawn due to %s", stderr
                 )
             content = stdout
             if script:
                 spl = content.split(script)
                 if len(spl) > 2:
-                    logger.warning("[mermaidjs] too many output lines %s", content)
+                    logger.warning("[runmermaid] too many output lines %s", content)
                 content = spl[-1]
 
-        node = mermaidjs_node(code=content, options={"docname": docname})
+        node = runmermaid_node(code=content, options={"docname": docname})
         return [node]
 
 
@@ -140,8 +140,8 @@ def run(self):
 # ---------------------------------------------------------------------------
 
 
-def visit_mermaidjs_node_html(self, node):
-    """Render the mermaidjs node in HTML output."""
+def visit_runmermaid_node_html(self, node):
+    """Render the runmermaid node in HTML output."""
     code = node["code"].strip()
     # Emit a 
 block; mermaid.js will replace it at runtime.
     self.body.append(
@@ -152,42 +152,42 @@ def visit_mermaidjs_node_html(self, node):
     raise nodes.SkipNode
 
 
-def depart_mermaidjs_node_html(self, node):
-    """Depart the mermaidjs HTML node. Not called because the visitor raises SkipNode."""
+def depart_runmermaid_node_html(self, node):
+    """Depart the runmermaid HTML node. Not called because the visitor raises SkipNode."""
 
 
-def visit_mermaidjs_node_rst(self, node):
-    """Render the mermaidjs node in RST output."""
+def visit_runmermaid_node_rst(self, node):
+    """Render the runmermaid node in RST output."""
     self.new_state(0)
-    self.add_text(".. mermaidjs::" + self.nl)
+    self.add_text(".. runmermaid::" + self.nl)
     self.new_state(self.indent)
     for row in node["code"].split("\n"):
         self.add_text(row + self.nl)
 
 
-def depart_mermaidjs_node_rst(self, node):
-    """Depart mermaidjs node in RST output."""
+def depart_runmermaid_node_rst(self, node):
+    """Depart runmermaid node in RST output."""
     self.end_state()
     self.end_state(wrap=False)
 
 
-def visit_mermaidjs_node_text(self, node):
-    """Render the mermaidjs node in plain-text output."""
+def visit_runmermaid_node_text(self, node):
+    """Render the runmermaid node in plain-text output."""
     self.new_state(0)
-    self.add_text("[mermaidjs diagram]\n")
+    self.add_text("[runmermaid diagram]\n")
     self.new_state(self.indent)
     for row in node["code"].split("\n"):
         self.add_text(row + self.nl)
 
 
-def depart_mermaidjs_node_text(self, node):
-    """Depart mermaidjs node in text output."""
+def depart_runmermaid_node_text(self, node):
+    """Depart runmermaid node in text output."""
     self.end_state()
     self.end_state(wrap=False)
 
 
-def visit_mermaidjs_node_latex(self, node):
-    """Render the mermaidjs node in LaTeX output (verbatim source)."""
+def visit_runmermaid_node_latex(self, node):
+    """Render the runmermaid node in LaTeX output (verbatim source)."""
     code = node["code"].strip()
     self.body.append("\n\\begin{verbatim}\n")
     self.body.append(code)
@@ -195,8 +195,8 @@ def visit_mermaidjs_node_latex(self, node):
     raise nodes.SkipNode
 
 
-def depart_mermaidjs_node_latex(self, node):
-    """Depart the mermaidjs LaTeX node. Not called because the visitor raises SkipNode."""
+def depart_runmermaid_node_latex(self, node):
+    """Depart the runmermaid LaTeX node. Not called because the visitor raises SkipNode."""
 
 
 # ---------------------------------------------------------------------------
@@ -223,19 +223,19 @@ def add_mermaid_js(app):
 
 def setup(app):
     """
-    setup for ``mermaidjs`` (sphinx)
+    setup for ``runmermaid`` (sphinx)
     """
     app.connect("builder-inited", add_mermaid_js)
 
     app.add_node(
-        mermaidjs_node,
-        html=(visit_mermaidjs_node_html, depart_mermaidjs_node_html),
-        epub=(visit_mermaidjs_node_html, depart_mermaidjs_node_html),
-        latex=(visit_mermaidjs_node_latex, depart_mermaidjs_node_latex),
-        text=(visit_mermaidjs_node_text, depart_mermaidjs_node_text),
-        rst=(visit_mermaidjs_node_rst, depart_mermaidjs_node_rst),
-        md=(visit_mermaidjs_node_text, depart_mermaidjs_node_text),
+        runmermaid_node,
+        html=(visit_runmermaid_node_html, depart_runmermaid_node_html),
+        epub=(visit_runmermaid_node_html, depart_runmermaid_node_html),
+        latex=(visit_runmermaid_node_latex, depart_runmermaid_node_latex),
+        text=(visit_runmermaid_node_text, depart_runmermaid_node_text),
+        rst=(visit_runmermaid_node_rst, depart_runmermaid_node_rst),
+        md=(visit_runmermaid_node_text, depart_runmermaid_node_text),
     )
 
-    app.add_directive("mermaidjs", MermaidDirective)
+    app.add_directive("runmermaid", RunMermaidDirective)
     return {"version": sphinx.__display_version__, "parallel_read_safe": True}

From ac99a8b47893b60ea427cdb8d21c3a9b426a6703 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 4 May 2026 09:51:30 +0000
Subject: [PATCH 6/7] move runmermaid after quote in api index

Agent-Logs-Url: https://github.com/sdpython/sphinx-runpython/sessions/08981185-c76f-49f9-bc90-02ba60e6c5ac

Co-authored-by: xadupre <22452781+xadupre@users.noreply.github.com>
---
 _doc/api/index.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/_doc/api/index.rst b/_doc/api/index.rst
index f382c62..3bbee13 100644
--- a/_doc/api/index.rst
+++ b/_doc/api/index.rst
@@ -15,8 +15,8 @@ Extensions
     docassert
     epkg
     gdot
-    runmermaid
     quote
+    runmermaid
     runpython
     tools
     rst_builder

From 6eef81e853a5d9e7e3dc28ed7cf9ed5969f623b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Xavier=20Dupr=C3=A9?= 
Date: Mon, 4 May 2026 13:02:01 +0200
Subject: [PATCH 7/7] fix

---
 .../runmermaid/sphinx_runmermaid_extension.py            | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py b/sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py
index 87b806a..11f19b9 100644
--- a/sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py
+++ b/sphinx_runpython/runmermaid/sphinx_runmermaid_extension.py
@@ -36,7 +36,7 @@ class RunMermaidDirective(Directive):
       ``stdout`` contains the diagram definition.
     * *process*: run the Python script in a separate process.
 
-    Example – inline diagram::
+    Example - inline diagram::
 
         .. runmermaid::
 
@@ -50,7 +50,7 @@ class RunMermaidDirective(Directive):
         graph LR
             A --> B --> C
 
-    Example – script-generated diagram::
+    Example - script-generated diagram::
 
         .. runmermaid::
             :script:
@@ -208,11 +208,12 @@ def add_mermaid_js(app):
     """Inject the Mermaid JS library into HTML pages."""
     if app.builder.format != "html":
         return
-    app.add_js_file(_MERMAID_JS_URL, **{"loading_method": "async"})
+    app.add_js_file(_MERMAID_JS_URL, loading_method="async")
     # Initialise mermaid after the DOM is ready.
     app.add_js_file(
         None,
-        body="document.addEventListener('DOMContentLoaded', function() { mermaid.initialize({startOnLoad: true}); });",
+        body="document.addEventListener('DOMContentLoaded', function() "
+        "{ mermaid.initialize({startOnLoad: true}); });",
     )