@beoe/rehype-vizdom
TypeScript icon, indicating that this package has built-in type declarations

0.0.3 • Public • Published

@beoe/rehype-vizdom

Rehype plugin to generate Vizdom diagrams (as inline SVGs) in place of code fences. This

```vizdom
digraph G { Hello -> World }
```

will be converted to

<figure class="beoe vizdom">
  <svg>...</svg>
</figure>

which can look like this:

TODO: add screenshot

Usage

import rehypeVizdom from "@beoe/rehype-vizdom";

const html = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypeVizdom)
  .use(rehypeStringify)
  .process(`markdown`);

It support caching the same way as @beoe/rehype-code-hook does.

Tips

Dark mode

You can add dark mode with something like this:

:root {
  --sl-color-white: #000;
  --sl-color-black: #fff;
}
@media (prefers-color-scheme: dark) {
  :root {
    --sl-color-white: #fff;
    --sl-color-black: #000;
  }
}
.vizdom {
  :not([fill]) {
    fill: var(--sl-color-white);
  }
  [fill="black"],
  [fill="#000"] {
    fill: var(--sl-color-white);
  }
  [stroke="black"],
  [stroke="#000"] {
    stroke: var(--sl-color-white);
  }
  [fill="white"],
  [fill="#fff"] {
    fill: var(--sl-color-black);
  }
  [stroke="white"],
  [stroke="#fff"] {
    stroke: var(--sl-color-black);
  }
}

Add CSS class to diagram

You have two options:

  • either globally
    .use(rehypeVizdom, { class: "something" })
  • or locally
    ```vizdom class=something
    digraph G { Hello -> World }
    ```

If you're using @tailwindcss/typography, it is probably a good idea to add not-content globally.

Add links

Inline SVG can contain HTML links:

```vizdom
digraph G {
 node[URL="https://example.com"]
}
```

Rounded corners for rectangles

  1. Disable svgo or provide your own configuration with convertShapeToPath: false
    • either globally
      .use(rehypeVizdom, { svgo: false })
    • or locally
      ```vizdom svgo=false
      digraph G { Hello -> World }
      ```
  2. Add CSS
    .vizdom {
      .node rect {
        rx: 7px;
      }
    }

Client side interactivity

demo

  1. Set option to generate data-graph HTML attribute

    • either globally
      .use(rehypeVizdom, { datagraph: 'dagre' })
    • or locally
      ```vizdom datagraph=dagre
      digraph G { Hello -> World }
      ```
  2. Add JS

    import { json, alg, type Path } from "@dagrejs/graphlib";
    
    type D = { [node: string]: Path };
    
    document.querySelectorAll(".vizdom").forEach((container) => {
      const data = container.getAttribute("data-graph")
        ? JSON.parse(container.getAttribute("data-graph")!)
        : null;
    
      if (!data) return;
      const graph = json.read(data);
    
      function clear() {
        container.querySelectorAll(".node").forEach((node) => {
          node.classList.remove("active");
          node.classList.remove("selected");
        });
        container
          .querySelectorAll(".edge")
          .forEach((node) => node.classList.remove("active"));
      }
    
      function highlight(id: string) {
        alg.postorder(graph, [id]).forEach((node) => {
          container.querySelector(`#node-${node}`)?.classList.add("active");
          graph.outEdges(node)?.forEach(({ name }) => {
            container.querySelector(`#edge-${name}`)?.classList.add("active");
          });
        });
      }
    
      // highlight on hover
      let currentHover: string | null = null;
      container.addEventListener("mouseover", (e) => {
        // @ts-expect-error
        const node = e.target?.closest(".node");
        if (node) {
          const id = node.getAttribute("id").replace("node-", "");
          if (currentHover == id) return;
          clear();
          highlight(id);
          currentHover = id;
        } else {
          if (currentHover == null) return;
          clear();
          currentHover = null;
        }
      });
    });
  3. Add CSS

    @keyframes dash {
      from {
        stroke-dashoffset: 40;
      }
      to {
        stroke-dashoffset: 0;
      }
    }
    .vizdom[data-graph] {
      .edge.active a path:first-child {
        stroke-dasharray: 5 5;
        animation-name: dash;
        animation-duration: 1000ms;
        stroke-dashoffset: 0;
        animation-iteration-count: infinite;
        animation-timing-function: linear;
      }
    
      .node {
        cursor: pointer;
      }
    
      .node.selected a *:first-child {
        stroke-width: 2px;
      }
    }
    
    @media (prefers-reduced-motion) {
      .vizdom[data-graph] {
        .edge.active a path:first-child {
          animation-duration: 4000ms;
        }
      }
    }

Notes

  • Text is transformed to path, so Cmd + F doesn't work
    • alternatively one can generate JSON representation of graph and search through labels with text-search
  • Doesn't support some unicode chars, like label="∅"
  • Client-side JS library

Dependencies (5)

Dev Dependencies (5)

Package Sidebar

Install

npm i @beoe/rehype-vizdom

Weekly Downloads

6

Version

0.0.3

License

MIT

Unpacked Size

12.6 kB

Total Files

6

Last publish

Collaborators

  • stereobooster