@md-plugins/quasar-app-extension-q-press 0.1.0-beta.19 → 0.1.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +1 -1
  2. package/dist/install.js +1 -0
  3. package/dist/templates/init/src/_q-press/components/MarkdownMermaid.vue +123 -0
  4. package/dist/templates/init/src/_q-press/css/code-theme.scss +12 -0
  5. package/dist/templates/init/src/markdown/getting-started/introduction.md +2 -0
  6. package/dist/templates/init/src/markdown/md-plugins/codeblocks/overview.md +16 -2
  7. package/dist/templates/init/src/markdown/md-plugins/mermaid/advanced.md +69 -0
  8. package/dist/templates/init/src/markdown/md-plugins/mermaid/overview.md +73 -0
  9. package/dist/templates/init/src/markdown/vite-plugins/vite-md-plugin/advanced.md +84 -54
  10. package/dist/templates/init/src/markdown/vite-plugins/vite-md-plugin/overview.md +1 -0
  11. package/dist/templates/init/src/siteConfig/index.ts +7 -0
  12. package/dist/templates/update/src/_q-press/components/MarkdownMermaid.vue +123 -0
  13. package/dist/templates/update/src/_q-press/css/code-theme.scss +12 -0
  14. package/package.json +14 -13
  15. package/src/install.ts +1 -0
  16. package/src/templates/init/src/_q-press/components/MarkdownMermaid.vue +123 -0
  17. package/src/templates/init/src/_q-press/css/code-theme.scss +12 -0
  18. package/src/templates/init/src/markdown/getting-started/introduction.md +2 -0
  19. package/src/templates/init/src/markdown/md-plugins/codeblocks/overview.md +16 -2
  20. package/src/templates/init/src/markdown/md-plugins/mermaid/advanced.md +69 -0
  21. package/src/templates/init/src/markdown/md-plugins/mermaid/overview.md +73 -0
  22. package/src/templates/init/src/markdown/vite-plugins/vite-md-plugin/advanced.md +84 -54
  23. package/src/templates/init/src/markdown/vite-plugins/vite-md-plugin/overview.md +1 -0
  24. package/src/templates/init/src/siteConfig/index.ts +7 -0
  25. package/src/templates/update/src/_q-press/components/MarkdownMermaid.vue +123 -0
  26. package/src/templates/update/src/_q-press/css/code-theme.scss +12 -0
package/README.md CHANGED
@@ -4,7 +4,7 @@ The Ultimate Markdown Solution for the Quasar Framework.
4
4
 
5
5
  See the [documentation](https://md-plugins.netlify.app/quasar-app-extensions/qpress/overview) for more information.
6
6
 
7
- > Current beta release: `0.1.0-beta.19`.
7
+ > Current beta release: `0.1.0-beta.20`.
8
8
  >
9
9
  > Q-Press currently targets Quasar Vite projects using `@quasar/app-vite` `>=3.0.0-beta.33`. TypeScript processing is required.
10
10
 
package/dist/install.js CHANGED
@@ -24,6 +24,7 @@ export default defineInstallScript(async (api) => {
24
24
  }
25
25
  api.extendPackageJson({
26
26
  dependencies: {
27
+ mermaid: '^11.15.0',
27
28
  shiki: '^4.1.0',
28
29
  },
29
30
  });
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <div class="markdown-mermaid q-my-md">
3
+ <div
4
+ v-if="errorMessage"
5
+ class="markdown-mermaid__error"
6
+ role="alert"
7
+ >
8
+ {{ errorMessage }}
9
+ </div>
10
+ <div
11
+ v-else-if="isRendering"
12
+ class="markdown-mermaid__loading"
13
+ >
14
+ Rendering diagram...
15
+ </div>
16
+ <div
17
+ ref="containerRef"
18
+ class="markdown-mermaid__diagram"
19
+ ></div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import { uid, useQuasar } from 'quasar'
25
+ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
26
+
27
+ const props = defineProps<{
28
+ code: string
29
+ }>()
30
+
31
+ const $q = useQuasar()
32
+ const containerRef = ref<HTMLElement | null>(null)
33
+ const errorMessage = ref('')
34
+ const isRendering = ref(false)
35
+
36
+ let disposed = false
37
+ let renderRequest = 0
38
+
39
+ async function renderDiagram(): Promise<void> {
40
+ if (import.meta.env.QUASAR_CLIENT !== true || containerRef.value === null) {
41
+ return
42
+ }
43
+
44
+ const requestId = ++renderRequest
45
+
46
+ errorMessage.value = ''
47
+ isRendering.value = true
48
+
49
+ await nextTick()
50
+
51
+ try {
52
+ const { default: mermaid } = await import('mermaid')
53
+
54
+ mermaid.initialize({
55
+ securityLevel: 'strict',
56
+ startOnLoad: false,
57
+ theme: $q.dark.isActive === true ? 'dark' : 'default',
58
+ })
59
+
60
+ const { svg, bindFunctions } = await mermaid.render(`markdown-mermaid-${uid()}`, props.code)
61
+
62
+ if (disposed === true || requestId !== renderRequest || containerRef.value === null) {
63
+ return
64
+ }
65
+
66
+ containerRef.value.innerHTML = svg
67
+ bindFunctions?.(containerRef.value)
68
+ } catch (error) {
69
+ if (disposed === false && requestId === renderRequest) {
70
+ errorMessage.value = error instanceof Error ? error.message : 'Unable to render diagram.'
71
+ }
72
+ } finally {
73
+ if (disposed === false && requestId === renderRequest) {
74
+ isRendering.value = false
75
+ }
76
+ }
77
+ }
78
+
79
+ onMounted(() => {
80
+ void renderDiagram()
81
+ })
82
+
83
+ watch(
84
+ () => [props.code, $q.dark.isActive] as const,
85
+ () => {
86
+ void renderDiagram()
87
+ },
88
+ )
89
+
90
+ onBeforeUnmount(() => {
91
+ disposed = true
92
+ })
93
+ </script>
94
+
95
+ <style lang="scss">
96
+ .markdown-mermaid {
97
+ border: 1px solid var(--q-separator-color);
98
+ border-radius: 8px;
99
+ overflow-x: auto;
100
+ padding: 1rem;
101
+
102
+ &__diagram {
103
+ min-width: 100%;
104
+ text-align: center;
105
+
106
+ svg {
107
+ max-width: 100%;
108
+ height: auto;
109
+ }
110
+ }
111
+
112
+ &__error {
113
+ color: var(--q-negative);
114
+ font-family: monospace;
115
+ white-space: pre-wrap;
116
+ }
117
+
118
+ &__loading {
119
+ color: var(--q-secondary);
120
+ font-style: italic;
121
+ }
122
+ }
123
+ </style>
@@ -4,6 +4,10 @@
4
4
  overflow: auto;
5
5
  border-radius: inherit;
6
6
 
7
+ &.twoslash {
8
+ overflow: visible;
9
+ }
10
+
7
11
  code {
8
12
  display: block;
9
13
  padding: 16px;
@@ -39,6 +43,10 @@
39
43
  display: inline-block;
40
44
  width: 100%;
41
45
  }
46
+
47
+ &:has(.twoslash-query-persisted) > code {
48
+ padding-bottom: calc(16px + 2.2em);
49
+ }
42
50
  }
43
51
 
44
52
  .c-line {
@@ -165,6 +173,10 @@
165
173
  transform: translateY(1.5em);
166
174
  }
167
175
 
176
+ .twoslash-query-persisted {
177
+ display: inline-block;
178
+ }
179
+
168
180
  .twoslash-hover:hover .twoslash-popup-container,
169
181
  .twoslash-error-hover:hover .twoslash-popup-container,
170
182
  .twoslash-query-persisted .twoslash-popup-container,
@@ -23,6 +23,7 @@ The core Markdown-it plugins and direct Vite plugins work in Vue/Vite projects a
23
23
  - **Title Extraction**: Extract the first header in Markdown as the page title.
24
24
  - **Script Imports**: Extract and process **&lt;script import&gt;** blocks from Markdown.
25
25
  - **Code Block Enhancements**: Enhance code block rendering with syntax highlighting, tabs, and more.
26
+ - **Mermaid Diagrams**: Render Mermaid fenced code blocks as client-side diagrams.
26
27
  - **Custom Styling**: Apply custom styles to your Markdown content for a more refined look.
27
28
  - **Integration**: Easily integrate with other tools and platforms to streamline your workflow.
28
29
 
@@ -59,6 +60,7 @@ Here are some examples of what you can achieve with Markdown Plugins:
59
60
  - **Title Extraction**: Extract the first header in Markdown as the page title.
60
61
  - **Script Imports**: Extract and process `<script import>` blocks from Markdown.
61
62
  - **Code Block Enhancements**: Enhance code block rendering with syntax highlighting, tabs, and more.
63
+ - **Mermaid Diagrams**: Add flowcharts, sequence diagrams, and other Mermaid diagrams to Markdown pages.
62
64
 
63
65
  ## Support
64
66
 
@@ -68,16 +68,30 @@ console.log('Hello, world!')
68
68
 
69
69
  ### Code Block with TwoSlash
70
70
 
71
- Add the `twoslash` attribute to TypeScript or JavaScript examples when you want inferred type information, compiler diagnostics, or `^?` query output.
71
+ Add the `twoslash` attribute to TypeScript or JavaScript examples when you want inferred type information, compiler diagnostics, or query output. Hover an identifier in the example below to see the type tooltip.
72
72
 
73
73
  ```ts [twoslash]
74
74
  const count = 1
75
- // ^?
75
+ const label = count.toFixed(0)
76
76
  ```
77
77
 
78
78
  ````markup
79
79
  ```ts [twoslash]
80
80
  const count = 1
81
+ const label = count.toFixed(0)
82
+ ```
83
+ ````
84
+
85
+ If you add a `// ^?` query marker, TwoSlash renders that query result persistently below the matching expression. That is useful for teaching types directly in the page, while normal identifier details remain hover-based.
86
+
87
+ ```ts [twoslash]
88
+ const selectedIcon = 'event' as const
89
+ // ^?
90
+ ```
91
+
92
+ ````markup
93
+ ```ts [twoslash]
94
+ const selectedIcon = 'event' as const
81
95
  // ^?
82
96
  ```
83
97
  ````
@@ -0,0 +1,69 @@
1
+ ---
2
+ title: Mermaid Advanced Topics
3
+ desc: Mermaid plugin options and integration details.
4
+ ---
5
+
6
+ The Mermaid plugin is small, but it has two different integration paths:
7
+
8
+ - **Component mode** is the Q-Press default. It emits `MarkdownMermaid` and lets Vue render Mermaid on the client.
9
+ - **Pre mode** emits a plain `<pre class="mermaid">` block for custom MarkdownIt pipelines.
10
+
11
+ ## Options
12
+
13
+ | Option | Type | Default | Description |
14
+ | --------------- | ---------------------- | -------------------- | -------------------------------------------- |
15
+ | `languages` | `string[]` | `['mermaid', 'mmd']` | Fence languages treated as Mermaid diagrams. |
16
+ | `renderMode` | `'component' \| 'pre'` | `'component'` | Output mode for diagrams. |
17
+ | `componentName` | `string` | `'MarkdownMermaid'` | Component used in component mode. |
18
+ | `codeProp` | `string` | `'code'` | Component prop that receives Mermaid source. |
19
+ | `preClass` | `string` | `'mermaid'` | CSS class used in pre mode. |
20
+ | `pageScripts` | `string[]` | Q-Press import | Imports added to generated Vue pages. |
21
+
22
+ ## Component Mode
23
+
24
+ Use component mode when Markdown output is compiled into Vue components:
25
+
26
+ ```ts
27
+ import MarkdownIt from 'markdown-it'
28
+ import { mermaidPlugin } from '@md-plugins/md-plugin-mermaid'
29
+
30
+ const md = new MarkdownIt()
31
+ md.use(mermaidPlugin, {
32
+ componentName: 'MarkdownMermaid',
33
+ codeProp: 'code',
34
+ })
35
+ ```
36
+
37
+ Component mode adds this import to the generated page when a Mermaid fence is found:
38
+
39
+ ```ts
40
+ import MarkdownMermaid from '@/.q-press/components/MarkdownMermaid.vue'
41
+ ```
42
+
43
+ ## Pre Mode
44
+
45
+ Use pre mode when your app has its own Mermaid bootstrapping:
46
+
47
+ ```ts
48
+ import MarkdownIt from 'markdown-it'
49
+ import { mermaidPlugin } from '@md-plugins/md-plugin-mermaid'
50
+
51
+ const md = new MarkdownIt()
52
+ md.use(mermaidPlugin, {
53
+ renderMode: 'pre',
54
+ preClass: 'mermaid',
55
+ })
56
+ ```
57
+
58
+ Then run Mermaid after the HTML is mounted:
59
+
60
+ ```ts
61
+ import mermaid from 'mermaid'
62
+
63
+ mermaid.initialize({ startOnLoad: false })
64
+ await mermaid.run({ querySelector: '.mermaid' })
65
+ ```
66
+
67
+ ## Security
68
+
69
+ The Q-Press `MarkdownMermaid` component initializes Mermaid with `securityLevel: 'strict'`. Keep that unless you have a specific reason to allow looser rendering.
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Mermaid Plugin
3
+ desc: Render Mermaid diagrams from fenced code blocks.
4
+ ---
5
+
6
+ The Mermaid plugin turns `mermaid` and `mmd` fenced code blocks into diagrams. In Q-Press and `viteMdPlugin`, Mermaid diagrams render through the generated `MarkdownMermaid` Vue component so diagrams stay client-side and SSR-safe.
7
+
8
+ ## Example
9
+
10
+ ```mermaid
11
+ flowchart LR
12
+ Markdown[Markdown file] --> Plugin[md-plugin-mermaid]
13
+ Plugin --> Component[MarkdownMermaid]
14
+ Component --> Diagram[Rendered diagram]
15
+ ```
16
+
17
+ ## Markdown
18
+
19
+ ````markdown
20
+ ```mermaid
21
+ flowchart LR
22
+ Markdown[Markdown file] --> Plugin[md-plugin-mermaid]
23
+ Plugin --> Component[MarkdownMermaid]
24
+ Component --> Diagram[Rendered diagram]
25
+ ```
26
+ ````
27
+
28
+ ## Installation
29
+
30
+ You can install the Mermaid plugin using npm, yarn, pnpm, or bun:
31
+
32
+ ```tabs
33
+ <<| bash pnpm |>>
34
+ pnpm add @md-plugins/md-plugin-mermaid mermaid
35
+ <<| bash bun |>>
36
+ bun add @md-plugins/md-plugin-mermaid mermaid
37
+ <<| bash yarn |>>
38
+ yarn add @md-plugins/md-plugin-mermaid mermaid
39
+ <<| bash npm |>>
40
+ npm install @md-plugins/md-plugin-mermaid mermaid
41
+ ```
42
+
43
+ Q-Press installs `mermaid` for generated documentation projects. Direct MarkdownIt users only need `mermaid` if they render diagrams in the browser.
44
+
45
+ ## Basic Usage
46
+
47
+ ```ts
48
+ import MarkdownIt from 'markdown-it'
49
+ import { mermaidPlugin } from '@md-plugins/md-plugin-mermaid'
50
+
51
+ const md = new MarkdownIt()
52
+ md.use(mermaidPlugin)
53
+ ```
54
+
55
+ The default output is a Vue component:
56
+
57
+ ```html
58
+ <MarkdownMermaid :code="diagramSource"></MarkdownMermaid>
59
+ ```
60
+
61
+ If you own the browser initialization yourself, use plain pre mode:
62
+
63
+ ```ts
64
+ md.use(mermaidPlugin, {
65
+ renderMode: 'pre',
66
+ })
67
+ ```
68
+
69
+ That renders Mermaid-compatible HTML:
70
+
71
+ ```html
72
+ <pre class="mermaid"><code>flowchart LR...</code></pre>
73
+ ```
@@ -11,7 +11,7 @@ The `viteMdPlugin` is a powerful tool for integrating Markdown processing into y
11
11
 
12
12
  ```ts
13
13
  import { Plugin } from 'vite'
14
- import { Options } from 'markdown-it'
14
+ import MarkdownIt, { Options } from 'markdown-it'
15
15
  import { BlockquotePluginOptions } from '@md-plugins/md-plugin-blockquote'
16
16
  import { CodeblockPluginOptions } from '@md-plugins/md-plugin-codeblocks'
17
17
  import { FrontmatterPluginOptions } from '@md-plugins/md-plugin-frontmatter'
@@ -19,21 +19,27 @@ import { HeadersPluginOptions } from '@md-plugins/md-plugin-headers'
19
19
  import { ImagePluginOptions } from '@md-plugins/md-plugin-image'
20
20
  import { InlineCodePluginOptions } from '@md-plugins/md-plugin-inlinecode'
21
21
  import { LinkPluginOptions } from '@md-plugins/md-plugin-link'
22
+ import { MermaidPluginOptions } from '@md-plugins/md-plugin-mermaid'
22
23
  import { TablePluginOptions } from '@md-plugins/md-plugin-table'
23
24
 
25
+ type MarkdownItPlugin = (md: MarkdownIt, ...params: any[]) => void
26
+ type MarkdownItPluginEntry = MarkdownItPlugin | [MarkdownItPlugin, ...any[]]
27
+
24
28
  interface MarkdownOptions extends Options {
25
29
  html?: boolean
26
30
  linkify?: boolean
27
31
  typographer?: boolean
28
32
  breaks?: boolean
29
- blockquote?: BlockquotePluginOptions
30
- codeblocks?: CodeblockPluginOptions
31
- frontmatter?: FrontmatterPluginOptions
32
- headers?: HeadersPluginOptions | boolean
33
- image?: ImagePluginOptions
34
- inlinecode?: InlineCodePluginOptions
35
- link?: LinkPluginOptions
36
- table?: TablePluginOptions
33
+ blockquotePlugin?: BlockquotePluginOptions
34
+ codeblockPlugin?: CodeblockPluginOptions
35
+ frontmatterPlugin?: FrontmatterPluginOptions
36
+ headersPlugin?: HeadersPluginOptions | boolean
37
+ imagePlugin?: ImagePluginOptions
38
+ inlineCodePlugin?: InlineCodePluginOptions
39
+ linkPlugin?: LinkPluginOptions
40
+ mermaidPlugin?: MermaidPluginOptions
41
+ tablePlugin?: TablePluginOptions
42
+ markdownItPlugins?: MarkdownItPluginEntry[]
37
43
  }
38
44
 
39
45
  interface MenuItem {
@@ -128,6 +134,59 @@ export default defineConfig({
128
134
  })
129
135
  ```
130
136
 
137
+ ### Using Other Markdown-It Plugins
138
+
139
+ The `viteMdPlugin` already includes the md-plugins used by Q-Press, plus inserted text support for `++text++`. If you need additional Markdown-it syntax, install the plugin and pass it through `config.markdownItPlugins`.
140
+
141
+ ```tabs
142
+ <<| bash pnpm |>>
143
+ pnpm add markdown-it-mark markdown-it-sub markdown-it-sup markdown-it-footnote
144
+ <<| bash bun |>>
145
+ bun add markdown-it-mark markdown-it-sub markdown-it-sup markdown-it-footnote
146
+ <<| bash yarn |>>
147
+ yarn add markdown-it-mark markdown-it-sub markdown-it-sup markdown-it-footnote
148
+ <<| bash npm |>>
149
+ npm install markdown-it-mark markdown-it-sub markdown-it-sup markdown-it-footnote
150
+ ```
151
+
152
+ ```ts
153
+ import { defineConfig } from 'vite'
154
+ import vue from '@vitejs/plugin-vue'
155
+ import markdownItFootnote from 'markdown-it-footnote'
156
+ import markdownItMark from 'markdown-it-mark'
157
+ import markdownItSub from 'markdown-it-sub'
158
+ import markdownItSup from 'markdown-it-sup'
159
+ import { viteMdPlugin } from '@md-plugins/vite-md-plugin'
160
+
161
+ export default defineConfig({
162
+ plugins: [
163
+ vue(),
164
+ viteMdPlugin({
165
+ path: './src/markdown',
166
+ menu: [],
167
+ config: {
168
+ markdownItPlugins: [
169
+ markdownItMark, // ==highlight==
170
+ markdownItSub, // H~2~O
171
+ markdownItSup, // E=mc^2^
172
+ markdownItFootnote, // footnote references
173
+ ],
174
+ },
175
+ }),
176
+ ],
177
+ })
178
+ ```
179
+
180
+ If you own a raw MarkdownIt instance instead of using `viteMdPlugin`, wire the plugins directly:
181
+
182
+ ```ts
183
+ import MarkdownIt from 'markdown-it'
184
+ import markdownItMark from 'markdown-it-mark'
185
+
186
+ const md = new MarkdownIt()
187
+ md.use(markdownItMark)
188
+ ```
189
+
131
190
  ### Quasar Framework Configuration
132
191
 
133
192
  If you’re using the Quasar Framework with Vite, additional configuration is needed to enable support for `.md` files:
@@ -210,55 +269,26 @@ export interface MenuItem {
210
269
  #### `config`
211
270
 
212
271
  - **Type**: `MarkdownOptions`
213
- - **Description**: Additional configuration options for the Markdown processing.
214
-
215
- ````ts
216
- import { Options } from 'markdown-it';
217
- import { BlockquotePluginOptions } from '@md-plugins/md-plugin-blockquote';
218
- import { CodeblockPluginOptions } from '@md-plugins/md-plugin-codeblocks';
219
- import { FrontmatterPluginOptions } from '@md-plugins/md-plugin-frontmatter';
220
- import { HeadersPluginOptions } from '@md-plugins/md-plugin-headers';
221
- import { ImagePluginOptions } from '@md-plugins/md-plugin-image';
222
- import { InlineCodePluginOptions } from '@md-plugins/md-plugin-inlinecode';
223
- import { LinkPluginOptions } from '@md-plugins/md-plugin-link';
224
- import { TablePluginOptions } from '@md-plugins/md-plugin-table';
225
-
226
- interface MarkdownOptions extends Options {
227
- html?: boolean;
228
- linkify?: boolean;
229
- typographer?: boolean;
230
- breaks?: boolean;
231
- blockquote?: BlockquotePluginOptions;
232
- codeblocks?: CodeblockPluginOptions;
233
- frontmatter?: FrontmatterPluginOptions;
234
- headers?: HeadersPluginOptions | boolean;
235
- image?: ImagePluginOptions;
236
- inlinecode?: InlineCodePluginOptions;
237
- link?: LinkPluginOptions;
238
- table?: TablePluginOptions;
239
- }
240
- ```
272
+ - **Description**: Additional configuration options for the Markdown processor and bundled md-plugins.
241
273
 
242
- ### Example Configuration
274
+ Use `config.markdownItPlugins` for extra Markdown-it syntax that md-plugins does not bundle by default. Use `config.mermaidPlugin` to customize Mermaid rendering.
243
275
 
244
- Here is an example of how you can configure the `viteMdPlugin` with custom options:
245
-
246
- ```typescript
247
- import { defineConfig } from 'vite'
248
- import vue from '@vitejs/plugin-vue'
276
+ ```ts
249
277
  import { viteMdPlugin } from '@md-plugins/vite-md-plugin'
250
-
251
- const menu = [
252
- { title: 'Home', path: '/' },
253
- { title: 'Guide', path: '/guide/' },
254
- { title: 'API', path: '/api/' },
255
- ]
256
- const basePath = '/docs'
257
-
258
- export default defineConfig({
259
- plugins: [vue(), viteMdPlugin(basePath, menu)],
278
+ import markdownItMark from 'markdown-it-mark'
279
+
280
+ viteMdPlugin({
281
+ path: './src/markdown',
282
+ menu: [],
283
+ config: {
284
+ markdownItPlugins: [markdownItMark],
285
+ mermaidPlugin: {
286
+ renderMode: 'component',
287
+ componentName: 'MarkdownMermaid',
288
+ },
289
+ },
260
290
  })
261
- ````
291
+ ```
262
292
 
263
293
  ### Using the Plugin
264
294
 
@@ -31,6 +31,7 @@ The `viteMdPlugin` is built on top of the following plugins:
31
31
  | `@md-plugins/md-plugin-inlinecode` | Adds a custom class to inline code blocks for styling. | [README](packages/md-plugin-inlinecode/README.md) | [Docs](/md-plugins/inline-code/overview) |
32
32
  | `@md-plugins/md-plugin-imports` | Extracts and processes `<script import>` blocks from Markdown. | [README](packages/md-plugin-imports/README.md) | [Docs](/md-plugins/imports/overview) |
33
33
  | `@md-plugins/md-plugin-link` | Converts Markdown links into Vue components for SPA-friendly routing. | [README](packages/md-plugin-link/README.md) | [Docs](/md-plugins/link/overview) |
34
+ | `@md-plugins/md-plugin-mermaid` | Renders Mermaid fenced code blocks as diagrams. | [README](packages/md-plugin-mermaid/README.md) | [Docs](/md-plugins/mermaid/overview) |
34
35
  | `@md-plugins/md-plugin-table` | Adds custom classes and attributes to Markdown tables. | [README](packages/md-plugin-table/README.md) | [Docs](/md-plugins/table/overview) |
35
36
  | `@md-plugins/md-plugin-title` | Extracts the first header in Markdown as the page title. | [README](packages/md-plugin-title/README.md) | [Docs](/md-plugins/title/overview) |
36
37
  | `@md-plugins/md-plugin-frontmatter` | Extracts and processes frontmatter content from Markdown files. | [README](packages/md-plugin-frontmatter/README.md) | [Docs](/md-plugins/frontmatter/overview) |
@@ -235,6 +235,13 @@ const mdPluginsMenu: SiteMenuItem = {
235
235
  { name: 'Advanced', path: '/md-plugins/link/advanced' },
236
236
  ],
237
237
  },
238
+ {
239
+ name: 'Mermaid',
240
+ children: [
241
+ { name: 'Overview', path: '/md-plugins/mermaid/overview' },
242
+ { name: 'Advanced', path: '/md-plugins/mermaid/advanced' },
243
+ ],
244
+ },
238
245
  {
239
246
  name: 'Table',
240
247
  children: [
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <div class="markdown-mermaid q-my-md">
3
+ <div
4
+ v-if="errorMessage"
5
+ class="markdown-mermaid__error"
6
+ role="alert"
7
+ >
8
+ {{ errorMessage }}
9
+ </div>
10
+ <div
11
+ v-else-if="isRendering"
12
+ class="markdown-mermaid__loading"
13
+ >
14
+ Rendering diagram...
15
+ </div>
16
+ <div
17
+ ref="containerRef"
18
+ class="markdown-mermaid__diagram"
19
+ ></div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup lang="ts">
24
+ import { uid, useQuasar } from 'quasar'
25
+ import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
26
+
27
+ const props = defineProps<{
28
+ code: string
29
+ }>()
30
+
31
+ const $q = useQuasar()
32
+ const containerRef = ref<HTMLElement | null>(null)
33
+ const errorMessage = ref('')
34
+ const isRendering = ref(false)
35
+
36
+ let disposed = false
37
+ let renderRequest = 0
38
+
39
+ async function renderDiagram(): Promise<void> {
40
+ if (import.meta.env.QUASAR_CLIENT !== true || containerRef.value === null) {
41
+ return
42
+ }
43
+
44
+ const requestId = ++renderRequest
45
+
46
+ errorMessage.value = ''
47
+ isRendering.value = true
48
+
49
+ await nextTick()
50
+
51
+ try {
52
+ const { default: mermaid } = await import('mermaid')
53
+
54
+ mermaid.initialize({
55
+ securityLevel: 'strict',
56
+ startOnLoad: false,
57
+ theme: $q.dark.isActive === true ? 'dark' : 'default',
58
+ })
59
+
60
+ const { svg, bindFunctions } = await mermaid.render(`markdown-mermaid-${uid()}`, props.code)
61
+
62
+ if (disposed === true || requestId !== renderRequest || containerRef.value === null) {
63
+ return
64
+ }
65
+
66
+ containerRef.value.innerHTML = svg
67
+ bindFunctions?.(containerRef.value)
68
+ } catch (error) {
69
+ if (disposed === false && requestId === renderRequest) {
70
+ errorMessage.value = error instanceof Error ? error.message : 'Unable to render diagram.'
71
+ }
72
+ } finally {
73
+ if (disposed === false && requestId === renderRequest) {
74
+ isRendering.value = false
75
+ }
76
+ }
77
+ }
78
+
79
+ onMounted(() => {
80
+ void renderDiagram()
81
+ })
82
+
83
+ watch(
84
+ () => [props.code, $q.dark.isActive] as const,
85
+ () => {
86
+ void renderDiagram()
87
+ },
88
+ )
89
+
90
+ onBeforeUnmount(() => {
91
+ disposed = true
92
+ })
93
+ </script>
94
+
95
+ <style lang="scss">
96
+ .markdown-mermaid {
97
+ border: 1px solid var(--q-separator-color);
98
+ border-radius: 8px;
99
+ overflow-x: auto;
100
+ padding: 1rem;
101
+
102
+ &__diagram {
103
+ min-width: 100%;
104
+ text-align: center;
105
+
106
+ svg {
107
+ max-width: 100%;
108
+ height: auto;
109
+ }
110
+ }
111
+
112
+ &__error {
113
+ color: var(--q-negative);
114
+ font-family: monospace;
115
+ white-space: pre-wrap;
116
+ }
117
+
118
+ &__loading {
119
+ color: var(--q-secondary);
120
+ font-style: italic;
121
+ }
122
+ }
123
+ </style>