lib/kino/mermaid.ex

defmodule Kino.Mermaid do
  @moduledoc ~S'''
  A kino for rendering Mermaid graphs.

  > #### Relation to Kino.Markdown {: .info}
  >
  > Mermaid graphs can also be generated dynamically with `Kino.Markdown`,
  > however the output of `Kino.Markdown` is never persisted in the
  > notebook source. `Kino.Mermaid` doesn't have this limitation.

  ## Examples

      Kino.Mermaid.new("""
      graph TD;
        A-->B;
        A-->C;
        B-->D;
        C-->D;
      """)

  '''

  use Kino.JS

  @type t :: Kino.JS.t()

  @doc """
  Creates a new kino displaying the given Mermaid graph.
  """
  @spec new(binary()) :: t()
  def new(content) do
    Kino.JS.new(__MODULE__, content, export_info_string: "mermaid")
  end

  asset "main.js" do
    """
    import "https://cdn.jsdelivr.net/npm/mermaid@9.1.3/dist/mermaid.min.js";

    mermaid.initialize({ startOnLoad: false });

    export function init(ctx, content) {
      function render() {
        mermaid.render("graph1", content, (svgSource, bindListeners) => {
          ctx.root.innerHTML = svgSource;
          bindListeners && bindListeners(ctx.root);

          // A workaround for https://github.com/mermaid-js/mermaid/issues/1758
          const svg = ctx.root.querySelector("svg");
          svg.removeAttribute("height");
        });
      }

      // If the JS view is not visible, defer initialization until it becomes visible
      if (window.innerWidth === 0) {
        window.addEventListener("resize", () => render(), { once: true });
      } else {
        render();
      }
    }
    """
  end
end