Recently was reminded by how crazy powerful and easy to use mermaid is. This article is a test for “wow mermaid can do that” type thing.
Given the following code snippet, this is coming directly from a “mermaid
" block in Notion. When it gets rendered, the markdown is processed with a pre.language-mermaid > code.language-mermaid
html block and code highlighted. Using the mermaid package, it enables us to pass in an html NodeList
to find what to render, and if everything is working as expected the mermaid chart should be visible below.
Example language-txt
of what the sequenceDiagram is:
sequenceDiagram
autonumber
participant Tony
participant MyDog
Tony->>MyDog: pet my dog
MyDog->>Tony: wags tail (is happy)
Below is the “mermaid
" version of the above script:
sequenceDiagram
autonumber
participant Tony
participant MyDog
Tony->>MyDog: pet my dog
MyDog->>Tony: wags tail (is happy)
Nuxt Snippet:
<template>
<section class="blog p-2 max-w-2xl m-y-auto">
<NuxtLink class="text-sm py-4" to="/bits"><i class="fa-solid fa-arrow-left"></i> back</NuxtLink>
<div v-if="page">
<h2>{{ page.title }}</h2>
<ClientOnly>
<code class="text-sm block mb-8">{{ useLongDate(page.created) }}</code>
</ClientOnly>
<div class="post-content" v-html="useMd(page?.page?.parent)"></div>
</div>
<div v-else>~ no content ~</div>
<NuxtLink class="text-center py-4" @click="scrollTop"><i class="fa-solid fa-arrow-up"></i> back to top</NuxtLink>
</section>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useAppStore } from '~/store/index';
const { scrollTop } = useUtils;
const { $renderMarkdown } = useNuxtApp();
const store = useAppStore();
const route = useRoute();
await store.fetchPage(route.params.slug as any);
const page = await store.getPage(route.params.slug)
useHead({
title: page.title,
meta: [
{ name: 'description', content: page.description },
{ property: 'og:title', content: page.title },
{ property: 'og:description', content: page.description },
{ name: 'twitter:title', content: page.title },
{ name: 'twitter:description', content: page.description },
]
})
useSeoMeta({
// will be inferred as the lastmod value in the sitemap
articleModifiedTime: page.last_edited_time
})
onMounted(() => {
const { $Prism } = useNuxtApp();
$Prism.highlightAll();
$renderMarkdown()
})
</script>
<style>
pre.language-mermaid {
background-color: var(--background, white)!important;
color: var(--text, black)!important;
}
</style>
plugins/mermaid.client.ts
a simple plugin to expose mermaid
to the application via Nuxt conventions, and a helper script for automatically rendering common code snippets for my posts, instead of copying and pasting that snippet everywhere.
import mermaid from 'mermaid';
export default defineNuxtPlugin(() => {
return {
provide: {
mermaid: mermaid,
// renderMarkdown automatically renders prisma code snippets for the mermaid type
renderMarkdown: async function () {
await mermaid.run({
nodes: document.querySelectorAll('code.language-mermaid'),
});
}
},
};
});
components/Mermaid.vue
This component wraps and renders provided slot text. Allows bespoke usage outside of just tagging class="mermaid"
, useful in certain circumstances
<template>
<div
v-if="show"
class="mermaid"
>
<slot />
</div>
</template>
<script setup lang="ts">
const show = ref(false);
const { $mermaid } = useNuxtApp();
onMounted(async () => {
show.value = true;
$mermaid.initialize({ startOnLoad: true });
await nextTick();
$mermaid.init();
});
</script>
Usage for standalone component
A couple examples on how to use the component
<template>
...
<Mermaid>{{ rawMermaidScript }}</Mermaid>
or inline:
<Mermaid>
sequenceDiagram
autonumber
participant Tony
participant MyDog
Tony->>MyDog: pet my dog
MyDog->>Tony: wags tail ferociously (is happy)
</Mermaid>
</template>