Appearance
@fc/vitepress-plugin-mermaid
Plugin VitePress para renderização de diagramas Mermaid com modal de zoom/pan ao clicar.
Funcionalidades
- Converte blocos
```mermaid ```em elementos renderizados client-side - Valida a sintaxe durante o build, com arquivo e linha no erro
- Renderiza sempre com
securityLevel: 'strict' - Exibe fallback legível quando o renderer do navegador fica temporariamente indisponível
- Suporte a tema claro/escuro sincronizado com o VitePress
- Modal de visualização com zoom in/out, restaurar e arrastar (via panzoom)
- Badge "⤢ ampliar" e anel de foco nos diagramas ao passar o mouse ou navegar por teclado
- Tooltips nos botões do modal
- Abre com clique, Enter ou Espaço; contém e restaura o foco; fecha com ESC ou clique fora
Instalação
Nas apps que consumirem o plugin, adicionar ao package.json:
json
"@fc/vitepress-plugin-mermaid": "workspace:*",
"panzoom": "catalog:docs"Uso
1. config.ts — registrar o plugin markdown-it
ts
import { markdownPluginMermaid } from '@fc/vitepress-plugin-mermaid'
import { defineConfig } from 'vitepress'
export default defineConfig({
markdown: {
config: (md) => md.use(markdownPluginMermaid),
},
})O script de build do site deve usar scripts/vitepress-com-ambiente.mjs, que chama validarDiagramasMermaidEmDiretorio antes de iniciar o VitePress e bloqueia a publicação em caso de erro:
json
"build": "node ../../scripts/vitepress-com-ambiente.mjs build site"Atenção:
config.tsé carregado pelo Node.js puro, sem Vite. Por isso, importe apenas de@fc/vitepress-plugin-mermaid(sem/theme). O subpath/themecontém Vue e.vuee só pode ser importado em arquivos processados pelo Vite.
2. theme/index.ts — ativar renderização e modal
ts
import { useMermaidTheme } from '@fc/vitepress-plugin-mermaid/theme'
import '@fc/vitepress-plugin-mermaid/mermaid.css'
import DefaultTheme from 'vitepress/theme'
import MyLayout from './MyLayout.vue'
export default {
extends: DefaultTheme,
Layout: MyLayout,
setup() {
useMermaidTheme()
},
}3. theme/MyLayout.vue — montar o modal no layout
vue
<script setup>
import MermaidModal from '@fc/vitepress-plugin-mermaid/modal'
import DefaultTheme from 'vitepress/theme'
const { Layout } = DefaultTheme
</script>
<template>
<Layout />
<MermaidModal />
</template>Exports
| Subpath | Export | Contexto |
|---|---|---|
@fc/vitepress-plugin-mermaid | markdownPluginMermaid | Node.js / configuração |
@fc/vitepress-plugin-mermaid/validacao | validarDiagramasMermaidEmDiretorio | Node.js / build |
@fc/vitepress-plugin-mermaid/theme | useMermaidTheme | theme/ (Vite / client-side) |
@fc/vitepress-plugin-mermaid/modal | export default de MermaidModal | MyLayout.vue |
@fc/vitepress-plugin-mermaid/mermaid.css | estilos de hover dos diagramas | theme/index.ts |
Arquitetura interna
src/
index.ts ← plugin markdown-it (puro TS, importável no config.ts)
validacao.ts ← varredura e parser cacheado executados antes do build
theme.ts ← useMermaidTheme
MermaidModal.vue ← componente modal com panzoom
mermaidState.ts ← ref<string|null> compartilhada entre theme.ts e MermaidModal
mermaid.css ← estilos .mermaid-clicavel (hover ring + badge "⤢ ampliar")Decisões técnicas
- Imports diretos por ambiente: o loader do VitePress carrega
config.tsviajiti(Node.js puro), antes do Vite inicializar. Importar arquivos.vuenesse contexto causaERR_UNKNOWN_FILE_EXTENSION. Por isso, plugin Markdown, validação Node, tema Vue e modal têm subpaths próprios, sem reexports que escondam a origem. - Dimensionamento pelo
viewBox:removeAttribute('width/height')num SVG dentro de umdiv inline-blockresulta em tamanho zero. O modal lêviewBox.baseValpara obter as dimensões naturais e escala proporcionalmente para caber em 88% do modal. - Reset sem
reset(): panzoom v9 não expõereset(). A restauração é feita commoveTo(0, 0)+zoomAbs(0, 0, 1). - Tooltips CSS:
::after { content: attr(title) }em vez do tooltip nativo do browser (delay de ~1s). clip-pathem vez deoverflow: hidden: o container do modal usaclip-path: inset(0 round 12px)para preservar os cantos arredondados sem criar um contexto de clipping que cortaria os tooltips CSS da toolbar.- Fonte idempotente: a DSL original fica preservada no elemento e cada geração tem uma época; uma troca de rota ou tema mais nova invalida resultados assíncronos antigos.
- Sintaxe bloqueada no build: o wrapper comum usa os tokens do
markdown-itpara encontrar somente cercas Mermaid reais, percorre páginas e includes, usa o parser oficial do Mermaid em ambiente DOM isolado do processo do VitePress, serializado e cacheado por fonte, e falha com arquivo e linha. Esse portão valida sintaxe; links e callbacks são neutralizados pela configuração explícitasecurityLevel: 'strict'no build e no navegador. O fallback do cliente cobre apenas indisponibilidade transitória do renderer. - Diálogo acessível: o wrapper do diagrama é um botão semântico nomeado por
accTitlequando disponível; o modal recebe foco inicial, contém Tab/Shift+Tab e devolve o foco ao acionador ao fechar.