Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Mermaid.js diagramming and graphing tool #2701

Open
ericscheid opened this issue Feb 28, 2023 · 18 comments · May be fixed by #2740
Open

Integrate Mermaid.js diagramming and graphing tool #2701

ericscheid opened this issue Feb 28, 2023 · 18 comments · May be fixed by #2740
Labels

Comments

@ericscheid
Copy link
Collaborator

Your idea:

JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically.
https://mermaid.js.org/

TL;DR:

Write into your brew a code block using their [relatively] simple text syntax

```mermaid
graph LR
    A[Square Rect] -- Link text --> B((Circle))
    A --> C(Round Rect)
    B --> D{Rhombus}
    C --> D
```

and this magically appears:

graph LR
    A[Square Rect] -- Link text --> B((Circle))
    A --> C(Round Rect)
    B --> D{Rhombus}
    C --> D

There are multiple diagram types (flowchart, gantt, pie, etc), and each can also be styled visually via css.


In terms of coding to support this .. at it's simplest we just insert a <script> element (which scans the page looking for code blocks with a particular classname), that's pretty much it [technically, there's more — I'm saying we don't need to do anything complicated with rejigging the parser or renderer].

<script type="module">
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@9/dist/mermaid.esm.min.mjs';
  mermaid.initialize({ startOnLoad: true });
</script>
@ericscheid
Copy link
Collaborator Author

See also issue #936.

@ericscheid
Copy link
Collaborator Author

Technically, we'd need to call mermaid.initialize with a custom class name, since our MD parser wants to prefix the code tag with language-. That is, this in a brew:

```mermaid
graph LR
    A[Square Rect] -- Link text --> B((Circle))
    A --> C(Round Rect)
    B --> D{Rhombus}
    C --> D
```

.. gets rendered into html as the following (note the class on the <code> element)

<pre>
<code class="language-mermaid">graph LR
    A[Square Rect] -- Link text --&gt; B((Circle))
    A --&gt; C(Round Rect)
    B --&gt; D{Rhombus}
    C --&gt; D
</code>
</pre>

@ericscheid
Copy link
Collaborator Author

Ideally, we wouldn't insert the <script module> into every page, just those with mermaid code.

Either we look for code blocks tagged as mermaid, or we have a brew.metadata field for [X] enable mermaid diagrams.

Probably the former for the simplest user experience.

@ericscheid
Copy link
Collaborator Author

ericscheid commented Feb 28, 2023

Making this work on /share and /print pages is easy (see above).

Making this work in the editor preview pane is going to be painful, especially since our app scrubs/dumps the html dom and replaces with fresh renders on every edit.

As a minimum viable product (MVP: a product with enough features to attract early-adopter customers and validate a product idea), we'd implement only on /share and /print. Would be a bit of a pain when fitting diagrams to page-layout, but demonstrates the idea sufficiently. From there we can guage the interest in supporting in the editor preview.

@calculuschild
Copy link
Member

calculuschild commented Feb 28, 2023

We can probably just make a Marked.js extension that does this so pages don't need to individually import the Mermaid script.

For instance the Mermaid docs show one way of doing it.

https://mermaid.js.org/config/usage.html#example-of-a-marked-renderer

Marked has since evolved so we can do it as a separate extension (a separate NPM package so others can benefit) instead of trying to cram it in with our existing custom renderer stuff.

@ericscheid
Copy link
Collaborator Author

Making this work in the editor preview pane is going to be painful, especially since our app scrubs/dumps the html dom and replaces with fresh renders on every edit.

Part of the pain I allude to here is the processing time — we probably don't want Mermaid re-parsing/rendering on every keystroke of editing.

The Mermaid live editor demo has a useful UI widget
live sync: on/off/refresh

@MichielDeMey
Copy link
Contributor

I'm more than happy to tackle this feature since I've worked with Mermaid.js in the past!

I've already gone ahead and created a working version, but I'd like to add some default styling to fit the Dungeons & Dragons theme and aesthetics.

Screenshot 2023-03-01 at 17 39 29

@calculuschild
Copy link
Member

calculuschild commented Mar 1, 2023

Another thing to consider, if we make it a Marked.js extension, is we can probably change the container syntax somewhat. For example replace the ````mermaid` with something else more "user-friendly" (most would have no idea what "mermaid" means).

Regardless, @MichielDeMey does your working model use a Marked.js extension? I'm hoping to eventually convert all of our weird custom Markdown stuff into proper extension packages to make them easier to maintain. You can see an example of how we extended Tables to support column and row spanning: https://github.com/calculuschild/marked-extended-tables

@ericscheid
Copy link
Collaborator Author

we can probably change the container syntax somewhat. For example replace the ````mermaid` with something else more "user-friendly" (most would have no idea what "mermaid" means).

Possibly disagree. Writing these "mermaid" code blocks requires being familiar with the mermaid code standards, said syntax being available at the mermaid website. We're going to point them in that direction anyway.

At best (if using some new term) we solve the problem of "how do I draw flowcharts and timelines?" by answering "use a code bock tagged with drawing(sic)". It's not much better, but also then creates a 2nd problem of "what's the syntax".

On the other hand, leaving it as mermaid then asking about drawing stuff can be answered with "use a code bock tagged with mermaid, using mermaid syntax". Also, anyone viewing a brew source will see brew code for a chart and will see a code block tagged mermaid followed by a regular syntax .. and might possibly go ahead and google "mermaid flowchart" in hope of learning more about this weird new syntax (without necessarily knowing the "mermaid" is a Proper Noun for a code library).

@ericscheid
Copy link
Collaborator Author

I've already gone ahead and created a working version, but I'd like to add some default styling to fit the Dungeons & Dragons theme and aesthetics.

That's great (and fast!).

Does it regenerate all mermaid svgs every time the preview pane is updated?

Definitely agree a minimum style would be ideal. We could roll it out without that themed CSS though (and keep it unannounced) — that way those in the know can experiment with styles.

@calculuschild
Copy link
Member

Writing these "mermaid" code blocks requires being familiar with the mermaid code standards, said syntax being available at the mermaid website. We're going to point them in that direction anyway.

Mmmm... Good point.

@G-Ambatte
Copy link
Collaborator

G-Ambatte commented Mar 2, 2023

Some info for minimal styling, particularly looking at official examples:

There's some styling on this Reddit post - https://www.reddit.com/r/homebrewery/comments/gfapc2/suggestions_adventure_flowchart

Some examples from Waterdeep: Dungeon of the Mad Mage here - #936 (comment)

Here's a couple from Baldur's Gate: Descent into Avernus that I had on Imgur for reasons:

@MichielDeMey
Copy link
Contributor

Weighing in on the discussion here:

Writing these "mermaid" code blocks requires being familiar with the mermaid code standards, said syntax being available at the mermaid website. We're going to point them in that direction anyway.

Agreed. Whilst Mermaid syntax might frighten some casual users, it IS easy to learn and there are a lot of great resources online to learn about it.

Realistically, looking at some of the adventure flowcharts in the source books it's really nothing more than a linear path. We can add a simple example to the Homebrewery kitchensink and we'll have helped 90% of the casual users.

Regardless, @MichielDeMey does your working model use a Marked.js extension?

Yes, I'm developing a generic Marked.js extension so that we can simply require('marked-mermaid') and marked.use(MarkedMermaid()).

Does it regenerate all mermaid svgs every time the preview pane is updated?

The marked result is simply memoized, so it will only regenerate the specific Mermaid SVG when the source code of that specific block changes. In practice, I've not noticed a performance drop, however we could optimize the caching behaviour down the road if we notice that users start to use a lot of Mermaid graphs.

Let me know if you have any more questions.

@MichielDeMey
Copy link
Contributor

Looks like there are a few discrepancies between the MermaidAPI rendering and the classic bootstrapping.

/print using the traditional bootstrapping:
localhost_8000_print_N4482FvVNv_u

/share using the MermaidAPI:
localhost_8000_share_N4482FvVNv_u

Notice the fonts being cut off. This is the same as in the /edit view.

@ericscheid
Copy link
Collaborator Author

This might be due to fonts not being fully loaded when mermaid is invoked — IIRC there's a specific mention of the issue on the mermaid website (possibly when discussing icon fonts).

@MichielDeMey
Copy link
Contributor

After some rigorous debugging I've been able to pin down one potential issue preventing correct rendering in our scenario.

We're loading the Homebrew content in an iFrame. Since we only load the PHB CSS (or any other theme) in the iFrame, we need to find a way to render the Mermaid diagram inside of the iFrame whereas it's now loaded inside the parent container of the iFrame.

Once I remove the iFrame, it renders much better. (Still not perfect, but very close)
But for some reason, it messes up the Gantt chart rendering.

After iFrame removal on the /share route:
localhost_8000_share_N4482FvVNv_u (2)

I believe removing the iFrame is not really an option, since we'd lose the advantage of sandboxing code.

@calculuschild
Copy link
Member

Do you have a PR for this? I can take a look.

@MichielDeMey MichielDeMey linked a pull request Mar 16, 2023 that will close this issue
@MichielDeMey
Copy link
Contributor

@calculuschild I've pushed my code and created a WIP PR for people to play with: #2740

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants