mirror of https://github.com/YunYouJun/valaxy
feat: support mermaid
This commit is contained in:
parent
91747dd520
commit
8daff16c88
|
@ -85,6 +85,7 @@ declare module 'vue-router/auto/routes' {
|
|||
'/posts/long-toc': RouteRecordInfo<'/posts/long-toc', '/posts/long-toc', Record<never, never>, Record<never, never>>,
|
||||
'/posts/lots-of-images': RouteRecordInfo<'/posts/lots-of-images', '/posts/lots-of-images', Record<never, never>, Record<never, never>>,
|
||||
'/posts/markdown': RouteRecordInfo<'/posts/markdown', '/posts/markdown', Record<never, never>, Record<never, never>>,
|
||||
'/posts/mermaid': RouteRecordInfo<'/posts/mermaid', '/posts/mermaid', Record<never, never>, Record<never, never>>,
|
||||
'/posts/post-updated': RouteRecordInfo<'/posts/post-updated', '/posts/post-updated', Record<never, never>, Record<never, never>>,
|
||||
'/posts/redirect': RouteRecordInfo<'/posts/redirect', '/posts/redirect', Record<never, never>, Record<never, never>>,
|
||||
'/posts/test': RouteRecordInfo<'/posts/test', '/posts/test', Record<never, never>, Record<never, never>>,
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
---
|
||||
title: Mermaid
|
||||
---
|
||||
|
||||
- [Mermaid](https://mermaid.js.org/)
|
||||
|
||||
## Flowchart
|
||||
|
||||
```mermaid
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
|
||||
```txt
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
|
||||
## Sequence diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Alice
|
||||
participant Bob
|
||||
Alice->>John: Hello John, how are you?
|
||||
loop Healthcheck
|
||||
John->>John: Fight against hypochondria
|
||||
end
|
||||
Note right of John: Rational thoughts <br/>prevail!
|
||||
John-->>Alice: Great!
|
||||
John->>Bob: How about you?
|
||||
Bob-->>John: Jolly good!
|
||||
```
|
||||
|
||||
## Gantt diagram
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title Adding GANTT diagram to mermaid
|
||||
excludes weekdays 2014-01-10
|
||||
|
||||
section A section
|
||||
Completed task :done, des1, 2014-01-06,2014-01-08
|
||||
Active task :active, des2, 2014-01-09, 3d
|
||||
Future task : des3, after des2, 5d
|
||||
Future task2 : des4, after des3, 5d
|
||||
```
|
||||
|
||||
## Class diagram
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
Class01 <|-- AveryLongClass : Cool
|
||||
Class03 *-- Class04
|
||||
Class05 o-- Class06
|
||||
Class07 .. Class08
|
||||
Class09 --> C2 : Where am i?
|
||||
Class09 --* C3
|
||||
Class09 --|> Class07
|
||||
Class07 : equals()
|
||||
Class07 : Object[] elementData
|
||||
Class01 : size()
|
||||
Class01 : int chimp
|
||||
Class01 : int gorilla
|
||||
Class08 <--> C2: Cool label
|
||||
```
|
||||
|
||||
## Git graph
|
||||
|
||||
```mermaid
|
||||
gitGraph
|
||||
commit
|
||||
commit
|
||||
branch develop
|
||||
commit
|
||||
commit
|
||||
commit
|
||||
checkout main
|
||||
commit
|
||||
commit
|
||||
```
|
||||
|
||||
## Quadrant Chart
|
||||
|
||||
```mermaid
|
||||
quadrantChart
|
||||
title Reach and engagement of campaigns
|
||||
x-axis Low Reach --> High Reach
|
||||
y-axis Low Engagement --> High Engagement
|
||||
quadrant-1 We should expand
|
||||
quadrant-2 Need to promote
|
||||
quadrant-3 Re-evaluate
|
||||
quadrant-4 May be improved
|
||||
Campaign A: [0.3, 0.6]
|
||||
Campaign B: [0.45, 0.23]
|
||||
Campaign C: [0.57, 0.69]
|
||||
Campaign D: [0.78, 0.34]
|
||||
Campaign E: [0.40, 0.34]
|
||||
Campaign F: [0.35, 0.78]
|
||||
```
|
||||
|
||||
## XY Chart
|
||||
|
||||
```mermaid
|
||||
xychart-beta
|
||||
title "Sales Revenue"
|
||||
x-axis [jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec]
|
||||
y-axis "Revenue (in $)" 4000 --> 11000
|
||||
bar [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
line [5000, 6000, 7500, 8200, 9500, 10500, 11000, 10200, 9200, 8500, 7000, 6000]
|
||||
```
|
|
@ -5,7 +5,6 @@ import { addonAlgolia } from 'valaxy-addon-algolia'
|
|||
import { addonBangumi } from 'valaxy-addon-bangumi'
|
||||
import { addonComponents } from 'valaxy-addon-components'
|
||||
import { addonLightGallery } from 'valaxy-addon-lightgallery'
|
||||
import { addonMeting } from 'valaxy-addon-meting'
|
||||
import { addonTest } from 'valaxy-addon-test'
|
||||
import { addonWaline } from 'valaxy-addon-waline'
|
||||
|
||||
|
@ -85,14 +84,14 @@ export default defineValaxyConfig<ThemeConfig>({
|
|||
comment: true,
|
||||
}),
|
||||
addonLightGallery(),
|
||||
addonMeting({
|
||||
global: true,
|
||||
props: {
|
||||
id: '2049540645',
|
||||
server: 'netease',
|
||||
type: 'song',
|
||||
},
|
||||
}),
|
||||
// addonMeting({
|
||||
// global: true,
|
||||
// props: {
|
||||
// id: '2049540645',
|
||||
// server: 'netease',
|
||||
// type: 'song',
|
||||
// },
|
||||
// }),
|
||||
// addonTwikoo({
|
||||
// envId: 'https://twikoo.vercel.app',
|
||||
// }),
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Mermaid
|
||||
(auto transformed, you don't need to use this component directly)
|
||||
|
||||
Usage:
|
||||
|
||||
```mermaid
|
||||
pie
|
||||
"Dogs" : 386
|
||||
"Cats" : 85
|
||||
"Rats" : 15
|
||||
```
|
||||
-->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getCurrentInstance, ref, watch, watchEffect } from 'vue'
|
||||
|
||||
import { renderMermaid } from '../../modules/mermaid'
|
||||
import ShadowRoot from '../internals/ShadowRoot.vue'
|
||||
|
||||
import { isDark } from '../../composables'
|
||||
|
||||
const props = defineProps<{
|
||||
code: string
|
||||
scale?: number
|
||||
theme?: string
|
||||
}>()
|
||||
|
||||
const vm = getCurrentInstance()
|
||||
const el = ref<ShadowRoot>()
|
||||
const html = ref('')
|
||||
|
||||
watchEffect(async (onCleanup) => {
|
||||
let disposed = false
|
||||
onCleanup(() => {
|
||||
disposed = true
|
||||
})
|
||||
const svg = await renderMermaid(
|
||||
props.code || '',
|
||||
{
|
||||
theme: props.theme || (isDark.value ? 'dark' : undefined),
|
||||
...vm!.attrs,
|
||||
},
|
||||
)
|
||||
if (!disposed)
|
||||
html.value = svg
|
||||
})
|
||||
|
||||
const actualHeight = ref<number>()
|
||||
|
||||
watch(html, () => {
|
||||
actualHeight.value = undefined
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
const svgEl = el.value?.children?.[0] as SVGElement | undefined
|
||||
if (svgEl && svgEl.hasAttribute('viewBox') && actualHeight.value == null) {
|
||||
const v = Number.parseFloat(svgEl.getAttribute('viewBox')?.split(' ')[3] || '')
|
||||
actualHeight.value = Number.isNaN(v) ? undefined : v
|
||||
}
|
||||
}, { flush: 'post' })
|
||||
|
||||
watchEffect(() => {
|
||||
const svgEl = el.value?.children?.[0] as SVGElement | undefined
|
||||
if (svgEl != null && props.scale != null && actualHeight.value != null) {
|
||||
svgEl.setAttribute('height', `${actualHeight.value * props.scale}`)
|
||||
svgEl.removeAttribute('width')
|
||||
svgEl.removeAttribute('style')
|
||||
}
|
||||
}, { flush: 'post' })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ShadowRoot class="mermaid" :inner-html="html" @shadow="el = $event" />
|
||||
</template>
|
|
@ -0,0 +1,24 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref, watchEffect } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
innerHtml: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(event: 'shadow', div: ShadowRoot): void
|
||||
}>()
|
||||
|
||||
const el = ref<HTMLDivElement>()
|
||||
const shadow = computed(() => el.value ? (el.value.shadowRoot || el.value.attachShadow({ mode: 'open' })) : null)
|
||||
watchEffect(() => {
|
||||
if (shadow.value && props.innerHtml) {
|
||||
emit('shadow', shadow.value)
|
||||
shadow.value.innerHTML = props.innerHtml
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div ref="el" />
|
||||
</template>
|
|
@ -0,0 +1,32 @@
|
|||
import mermaid from 'mermaid'
|
||||
import { customAlphabet } from 'nanoid'
|
||||
import { decode } from 'js-base64'
|
||||
import { clearUndefined } from '@antfu/utils'
|
||||
import { isClient } from '@vueuse/core'
|
||||
import setupMermaid from '../setup/mermaid'
|
||||
|
||||
if (isClient) {
|
||||
mermaid.startOnLoad = false
|
||||
mermaid.initialize({ startOnLoad: false })
|
||||
}
|
||||
|
||||
const nanoid = customAlphabet('abcedfghicklmn', 10)
|
||||
const cache = new Map<string, string>()
|
||||
|
||||
export async function renderMermaid(encoded: string, options: any) {
|
||||
const key = encoded + JSON.stringify(options)
|
||||
const _cache = cache.get(key)
|
||||
if (_cache)
|
||||
return _cache
|
||||
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
...clearUndefined(setupMermaid() || {}),
|
||||
...clearUndefined(options),
|
||||
})
|
||||
const code = decode(encoded)
|
||||
const id = nanoid()
|
||||
const { svg } = await mermaid.render(id, code)
|
||||
cache.set(key, svg)
|
||||
return svg
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/* __imports__ */
|
||||
|
||||
import { defineMermaidSetup } from 'valaxy'
|
||||
import type { MermaidOptions } from '../types'
|
||||
|
||||
export default defineMermaidSetup(() => {
|
||||
// eslint-disable-next-line prefer-const
|
||||
let injection_return: MermaidOptions = {
|
||||
theme: 'default',
|
||||
}
|
||||
|
||||
/* __injections__ */
|
||||
|
||||
return injection_return
|
||||
})
|
|
@ -1,10 +1,20 @@
|
|||
import type { ViteSSGContext } from 'vite-ssg'
|
||||
import type { Awaitable } from '@antfu/utils'
|
||||
import type { MermaidOptions } from './types'
|
||||
|
||||
// todo dynamic mermaid import
|
||||
|
||||
export type AppContext = ViteSSGContext
|
||||
|
||||
export type AppSetup = (ctx: AppContext) => Awaitable<void>
|
||||
|
||||
// client
|
||||
export type MermaidSetup = () => Partial<MermaidOptions> | void
|
||||
|
||||
export function defineAppSetup(fn: AppSetup) {
|
||||
return fn
|
||||
}
|
||||
|
||||
export function defineMermaidSetup(fn: MermaidSetup) {
|
||||
return fn
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import type { ViteSSGContext } from 'vite-ssg'
|
||||
import type mermaid from 'mermaid'
|
||||
|
||||
export type UserModule = (ctx: ViteSSGContext) => void
|
||||
|
||||
export type MermaidOptions = (typeof mermaid.initialize) extends (a: infer A) => any ? A : never
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import type MarkdownIt from 'markdown-it'
|
||||
import { defaultCodeTheme } from '../../setup'
|
||||
import { highlight } from '../highlight'
|
||||
import type { ResolvedValaxyOptions } from '../../../../options'
|
||||
|
||||
/**
|
||||
* Escape `{{}}` in code block to prevent Vue interpret it, #99
|
||||
*/
|
||||
export function escapeVueInCode(md: string) {
|
||||
return md.replace(/{{(.*?)}}/g, '{{$1}}')
|
||||
}
|
||||
|
||||
export function setupShiki(mdIt: MarkdownIt, highlight: any) {
|
||||
mdIt.options.highlight = (code, lang = 'text', attrs) => {
|
||||
return highlight(code, lang, attrs)
|
||||
}
|
||||
}
|
||||
|
||||
export async function createMdItShikiPlugin(options?: ResolvedValaxyOptions) {
|
||||
const mdOptions = options?.config.markdown || {}
|
||||
const theme = mdOptions.theme ?? defaultCodeTheme
|
||||
const shikiHighlight = await highlight(theme, mdOptions)
|
||||
|
||||
return function (mdIt: MarkdownIt) {
|
||||
setupShiki(mdIt, shikiHighlight)
|
||||
}
|
||||
}
|
|
@ -10,8 +10,6 @@ import TaskLists from 'markdown-it-task-lists'
|
|||
// https://www.npmjs.com/package/markdown-it-image-figures
|
||||
import imageFigures from 'markdown-it-image-figures'
|
||||
|
||||
import { componentPlugin } from '@mdit-vue/plugin-component'
|
||||
|
||||
import {
|
||||
type HeadersPluginOptions,
|
||||
headersPlugin,
|
||||
|
@ -33,7 +31,6 @@ import { preWrapperPlugin } from './plugins/markdown-it/preWrapper'
|
|||
import { lineNumberPlugin } from './plugins/markdown-it/lineNumbers'
|
||||
import { snippetPlugin } from './plugins/markdown-it/snippet'
|
||||
import type { ThemeOptions } from './types'
|
||||
import { createMdItShikiPlugin } from './plugins/markdown-it/shiki'
|
||||
|
||||
export const defaultCodeTheme = { light: 'github-light', dark: 'github-dark' } as const as ThemeOptions
|
||||
|
||||
|
@ -51,12 +48,6 @@ export async function setupMarkdownPlugins(
|
|||
if (mdOptions.preConfig)
|
||||
mdOptions.preConfig(md)
|
||||
|
||||
// highlight
|
||||
const shikiPlugin = await createMdItShikiPlugin(options)
|
||||
md.use(shikiPlugin)
|
||||
|
||||
// mdit-vue plugins
|
||||
md.use(componentPlugin, { ...mdOptions.component })
|
||||
// custom plugins
|
||||
md.use(highlightLinePlugin)
|
||||
.use(preWrapperPlugin, { theme, siteConfig })
|
||||
|
|
|
@ -3,7 +3,8 @@ import Markdown from 'unplugin-vue-markdown/vite'
|
|||
import * as base64 from 'js-base64'
|
||||
|
||||
import type { ResolvedValaxyOptions } from '../../options'
|
||||
import { setupMarkdownPlugins } from '.'
|
||||
import { highlight } from './plugins/highlight'
|
||||
import { defaultCodeTheme, setupMarkdownPlugins } from '.'
|
||||
|
||||
/**
|
||||
* Transform Mermaid code blocks (render done on client side)
|
||||
|
@ -14,7 +15,7 @@ export function transformMermaid(md: string): string {
|
|||
code = code.trim()
|
||||
options = options.trim() || '{}'
|
||||
const encoded = base64.encode(code, true)
|
||||
return `<Mermaid :code="'${encoded}'" v-bind="${options}" />`
|
||||
return `<ValaxyMermaid :code="'${encoded}'" v-bind="${options}" />`
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -22,24 +23,26 @@ export async function createMarkdownPlugin(
|
|||
options: ResolvedValaxyOptions,
|
||||
): Promise<Plugin> {
|
||||
const mdOptions = options?.config.markdown || {}
|
||||
const theme = mdOptions.theme ?? defaultCodeTheme
|
||||
|
||||
return Markdown({
|
||||
include: [/\.md$/],
|
||||
wrapperClasses: '',
|
||||
// headEnabled: false,
|
||||
|
||||
frontmatter: true,
|
||||
frontmatterOptions: mdOptions.frontmatter || {},
|
||||
|
||||
// v-pre
|
||||
escapeCodeTagInterpolation: true,
|
||||
|
||||
// markdownItOptions: {
|
||||
// quotes: '""\'\'',
|
||||
// html: true,
|
||||
// xhtmlOut: true,
|
||||
// linkify: true,
|
||||
// ...mdOptions?.markdownItOptions,
|
||||
// },
|
||||
markdownItOptions: {
|
||||
quotes: '""\'\'',
|
||||
html: true,
|
||||
xhtmlOut: true,
|
||||
linkify: true,
|
||||
highlight: await highlight(theme, mdOptions),
|
||||
...mdOptions?.markdownItOptions,
|
||||
},
|
||||
|
||||
async markdownItSetup(mdIt) {
|
||||
// setup mdIt
|
||||
|
@ -65,6 +68,9 @@ export async function createMarkdownPlugin(
|
|||
|
||||
return code
|
||||
},
|
||||
|
||||
// frontmatterOptions componentOptions in mdOptions
|
||||
...mdOptions,
|
||||
},
|
||||
}) as Plugin
|
||||
}
|
||||
|
|
|
@ -12,9 +12,6 @@ import type anchorPlugin from 'markdown-it-anchor'
|
|||
|
||||
import type { KatexOptions } from 'katex'
|
||||
|
||||
import type {
|
||||
FrontmatterPluginOptions,
|
||||
} from '@mdit-vue/plugin-frontmatter'
|
||||
import type {
|
||||
HeadersPluginOptions,
|
||||
} from '@mdit-vue/plugin-headers'
|
||||
|
@ -23,9 +20,6 @@ import type { TocPluginOptions } from '@mdit-vue/plugin-toc'
|
|||
|
||||
// import type { lazyloadOptions } from './plugins/markdown-it/lazyload'
|
||||
|
||||
import type {
|
||||
ComponentPluginOptions,
|
||||
} from '@mdit-vue/plugin-component'
|
||||
import type { Blocks, ContainerOptions } from './plugins/markdown-it/container'
|
||||
|
||||
export type ThemeOptions =
|
||||
|
@ -105,11 +99,6 @@ export interface MarkdownOptions {
|
|||
|
||||
/* ==================== Markdown It Plugins ==================== */
|
||||
// mdit-vue plugins
|
||||
/**
|
||||
* Options for `@mdit-vue/plugin-frontmatter`
|
||||
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-frontmatter
|
||||
*/
|
||||
frontmatter?: FrontmatterPluginOptions
|
||||
/**
|
||||
* Options for `@mdit-vue/plugin-headers`
|
||||
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-headers
|
||||
|
@ -134,11 +123,6 @@ export interface MarkdownOptions {
|
|||
* Custom block configurations based on `markdown-it-container`
|
||||
*/
|
||||
blocks?: Blocks
|
||||
/**
|
||||
* Options for `@mdit-vue/plugin-component`
|
||||
* @see https://github.com/mdit-vue/mdit-vue/tree/main/packages/plugin-component
|
||||
*/
|
||||
component?: ComponentPluginOptions
|
||||
|
||||
/**
|
||||
* @see [markdown-it-image-figures](https://www.npmjs.com/package/markdown-it-image-figures)
|
||||
|
|
|
@ -106,6 +106,7 @@
|
|||
"markdown-it-table-of-contents": "^0.6.0",
|
||||
"markdown-it-task-lists": "^2.1.1",
|
||||
"medium-zoom": "^1.1.0",
|
||||
"mermaid": "^10.8.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"open": "10.0.3",
|
||||
"ora": "^8.0.1",
|
||||
|
|
|
@ -68,3 +68,9 @@ declare module '/@valaxyjs/redirects' {
|
|||
|
||||
export const useVueRouter: boolean
|
||||
}
|
||||
|
||||
declare module 'mermaid/dist/mermaid.esm.mjs' {
|
||||
import Mermaid from 'mermaid/dist/mermaid.d.ts'
|
||||
|
||||
export default Mermaid
|
||||
}
|
||||
|
|
749
pnpm-lock.yaml
749
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue