Render documentation diagrams as static Mermaid assets

This commit is contained in:
Ruslan Bakiev
2026-05-01 15:09:02 +07:00
parent b7a5018c6e
commit 542ad1b648
23 changed files with 1105 additions and 134 deletions

View File

@@ -6,7 +6,7 @@ const props = defineProps<{
chart: string;
}>();
const svg = ref('');
const container = ref<HTMLElement | null>(null);
const error = ref('');
let initialized = false;
@@ -20,7 +20,7 @@ function initMermaid() {
mermaid.initialize({
startOnLoad: false,
theme: 'neutral',
securityLevel: 'strict',
securityLevel: 'loose',
fontFamily: 'Inter, Arial, sans-serif',
flowchart: {
useMaxWidth: true,
@@ -34,7 +34,7 @@ function initMermaid() {
const source = computed(() => props.chart.trim());
async function renderDiagram() {
if (!import.meta.client) {
if (!import.meta.client || !container.value) {
return;
}
@@ -42,10 +42,13 @@ async function renderDiagram() {
initMermaid();
error.value = '';
diagramId += 1;
const { svg: result } = await mermaid.render(`mermaid-diagram-${diagramId}`, source.value);
svg.value = result;
container.value.removeAttribute('data-processed');
container.value.id = `mermaid-diagram-${diagramId}`;
container.value.textContent = source.value;
await mermaid.run({
nodes: [container.value],
});
} catch (cause) {
svg.value = '';
error.value = cause instanceof Error ? cause.message : 'Failed to render Mermaid diagram.';
}
}
@@ -62,7 +65,7 @@ watch(source, () => {
<template>
<div class="mermaid-diagram">
<div v-if="error" class="mermaid-diagram__error">{{ error }}</div>
<div v-else class="mermaid-diagram__canvas" v-html="svg" />
<pre v-else ref="container" class="mermaid-diagram__canvas mermaid" />
</div>
</template>
@@ -77,6 +80,8 @@ watch(source, () => {
.mermaid-diagram__canvas {
padding: 1rem;
margin: 0;
white-space: pre-wrap;
}
.mermaid-diagram__canvas :deep(svg) {

View File

@@ -1,15 +1,15 @@
export const diagramSources: Record<string, string> = {
'architecture-overview': `flowchart LR
Browser[Браузер клиента]
Frontend[web-frontend<br/>Nuxt 4 + Vue 3]
Proxy[/api/graphql proxy]
Backend[apollo-backend<br/>Apollo Server + Express]
Prisma[Prisma ORM]
Db[(PostgreSQL)]
Vault[Vault]
OneC[1С]
Bots[Telegram / MAX / email]
Dokploy[Dokploy]
Browser["Браузер клиента"]
Frontend["web-frontend<br/>Nuxt 4 + Vue 3"]
Proxy["/api/graphql proxy"]
Backend["apollo-backend<br/>Apollo Server + Express"]
Prisma["Prisma ORM"]
Db[("PostgreSQL")]
Vault["Vault"]
OneC["1С"]
Bots["Telegram / MAX / email"]
Dokploy["Dokploy"]
Browser --> Frontend
Frontend --> Proxy
@@ -22,32 +22,32 @@ export const diagramSources: Record<string, string> = {
Frontend -.deploy.-> Dokploy
Backend -.deploy.-> Dokploy`,
'component-map': `flowchart LR
subgraph UI[UI и маршруты]
Pages[app/pages]
Components[app/components]
Composables[app/composables]
Middleware[middleware / plugins]
subgraph UI["UI и маршруты"]
Pages["app/pages"]
Components["app/components"]
Composables["app/composables"]
Middleware["middleware / plugins"]
end
subgraph Transport[Контракт и transport]
Ops[graphql/operations]
Generated[generated.ts]
Apollo[Apollo client]
Api[server/api/graphql]
subgraph Transport["Контракт и transport"]
Ops["graphql/operations"]
Generated["generated.ts"]
Apollo["Apollo client"]
Api["server/api/graphql"]
end
subgraph Server[Backend logic]
Schema[schema.graphql]
Resolvers[resolvers.js]
Auth[auth.js / access.js]
Context[context.js]
Messenger[messenger / telegram / max]
subgraph Server["Backend logic"]
Schema["schema.graphql"]
Resolvers["resolvers.js"]
Auth["auth.js / access.js"]
Context["context.js"]
Messenger["messenger / telegram / max"]
end
subgraph Data[Data layer]
Prisma[prisma-client.js]
PrismaSchema[schema.prisma]
Db[(PostgreSQL)]
subgraph Data["Data layer"]
Prisma["prisma-client.js"]
PrismaSchema["schema.prisma"]
Db[("PostgreSQL")]
end
Pages --> Components
@@ -67,33 +67,33 @@ export const diagramSources: Record<string, string> = {
Prisma --> PrismaSchema
Prisma --> Db`,
'infrastructure-topology': `flowchart TB
subgraph Repo[Репозиторий fregat]
FrontendRepo[web-frontend]
BackendRepo[apollo-backend]
TgRepo[tg-bot]
MaxRepo[max-bot]
BonusRepo[bonus-bot]
WorkerRepo[hatchet-worker]
VaultRepo[vault]
subgraph Repo["Репозиторий fregat"]
FrontendRepo["web-frontend"]
BackendRepo["apollo-backend"]
TgRepo["tg-bot"]
MaxRepo["max-bot"]
BonusRepo["bonus-bot"]
WorkerRepo["hatchet-worker"]
VaultRepo["vault"]
end
subgraph Dokploy[Dokploy]
FrontendSvc[web-frontend service]
BackendSvc[apollo-backend service]
TgSvc[tg-bot service]
MaxSvc[max-bot service]
BonusSvc[bonus-bot service]
WorkerSvc[hatchet-worker service]
VaultSvc[vault service]
subgraph Dokploy["Dokploy"]
FrontendSvc["web-frontend service"]
BackendSvc["apollo-backend service"]
TgSvc["tg-bot service"]
MaxSvc["max-bot service"]
BonusSvc["bonus-bot service"]
WorkerSvc["hatchet-worker service"]
VaultSvc["vault service"]
end
subgraph Infra[Инфраструктурный контур]
VaultData[Vault raft storage /vault/data]
BackendDb[(PostgreSQL for app)]
HatchetEngine[Hatchet engine]
HatchetPg[(PostgreSQL for Hatchet)]
OneC[1С]
Tailscale[Tailscale SSH / diagnostics]
subgraph Infra["Инфраструктурный контур"]
VaultData["Vault raft storage /vault/data"]
BackendDb[("PostgreSQL for app")]
HatchetEngine["Hatchet engine"]
HatchetPg[("PostgreSQL for Hatchet")]
OneC["1С"]
Tailscale["Tailscale SSH / diagnostics"]
end
FrontendRepo --> FrontendSvc
@@ -160,78 +160,78 @@ export const diagramSources: Record<string, string> = {
User "1" --> "*" ReferralLink : referrals
CatalogProductTypeSetting --> Product : productType`,
dashboard: `flowchart TB
subgraph Page[Главная страница клиента]
Header[Header / навигация]
Quick[Быстрые действия]
Active[Актуальные заказы и заявки]
Notes[Последние уведомления]
Bonus[Бонусный блок]
subgraph Page["Главная страница клиента"]
Header["Header / навигация"]
Quick["Быстрые действия"]
Active["Актуальные заказы и заявки"]
Notes["Последние уведомления"]
Bonus["Бонусный блок"]
end
Header --> Quick --> Active --> Notes --> Bonus`,
'catalog-grid': `flowchart TB
subgraph Catalog[Каталог продукции]
Title[Заголовок раздела]
Search[Поиск]
Grid[Сетка карточек товарных направлений]
Cards[Упаковочный скотч | Алюминиевый скотч | Крепп | Вспененный | Двусторонний ПП | Двусторонний PVC]
subgraph Catalog["Каталог продукции"]
Title["Заголовок раздела"]
Search["Поиск"]
Grid["Сетка карточек товарных направлений"]
Cards["Упаковочный скотч | Алюминиевый скотч | Крепп | Вспененный | Двусторонний ПП | Двусторонний PVC"]
end
Title --> Search --> Grid --> Cards`,
'product-card': `flowchart TB
subgraph ProductPage[Карточка товара]
Title[Заголовок товара и навигация]
subgraph TopRow[Верхний блок]
Image[Изображение товара]
Params[Параметры выбора]
Action[SKU / действие В корзину]
subgraph ProductPage["Карточка товара"]
Title["Заголовок товара и навигация"]
subgraph TopRow["Верхний блок"]
Image["Изображение товара"]
Params["Параметры выбора"]
Action["SKU / действие В корзину"]
end
Help[Пояснения по параметрам]
Table[Таблица доступных вариантов]
Help["Пояснения по параметрам"]
Table["Таблица доступных вариантов"]
end
Title --> TopRow --> Help --> Table`,
cart: `flowchart TB
subgraph CartPage[Корзина]
Items[Список выбранных позиций]
Delivery[Адрес доставки]
Comment[Комментарий клиента]
Submit[Отправить заявку]
subgraph CartPage["Корзина"]
Items["Список выбранных позиций"]
Delivery["Адрес доставки"]
Comment["Комментарий клиента"]
Submit["Отправить заявку"]
end
Items --> Delivery --> Comment --> Submit`,
'client-order': `flowchart TB
subgraph ClientOrder[Карточка заявки / заказа]
Summary[Номер документа и статус]
Composition[Состав позиций]
Terms[Стоимость и условия поставки]
History[История изменений]
subgraph ClientOrder["Карточка заявки / заказа"]
Summary["Номер документа и статус"]
Composition["Состав позиций"]
Terms["Стоимость и условия поставки"]
History["История изменений"]
end
Summary --> Composition --> Terms --> History`,
'bonus-cabinet': `flowchart TB
subgraph BonusPage[Бонусный кабинет]
Balance[Текущий бонусный баланс]
History[История операций]
Referrals[Реферальные связи]
Action[Подача заявки на использование или вывод]
subgraph BonusPage["Бонусный кабинет"]
Balance["Текущий бонусный баланс"]
History["История операций"]
Referrals["Реферальные связи"]
Action["Подача заявки на использование или вывод"]
end
Balance --> History --> Referrals --> Action`,
'manager-order': `flowchart TB
subgraph ManagerOrder[Карточка обработки заявки]
Summary[Клиент / контрагент / менеджер]
Payload[Состав заявки или расчетный payload]
Terms[Стоимость и условия поставки]
Actions[Опубликовать условия / перевести в работу / отменить]
subgraph ManagerOrder["Карточка обработки заявки"]
Summary["Клиент / контрагент / менеджер"]
Payload["Состав заявки или расчетный payload"]
Terms["Стоимость и условия поставки"]
Actions["Опубликовать условия / перевести в работу / отменить"]
end
Summary --> Payload --> Terms --> Actions`,
'manager-orders': `flowchart TB
subgraph ManagerOrders[Список заказов менеджера]
Header[Заголовок раздела]
Filters[Фильтры: статус / клиент / период]
Table[Таблица заказов]
subgraph ManagerOrders["Список заказов менеджера"]
Header["Заголовок раздела"]
Filters["Фильтры: статус / клиент / период"]
Table["Таблица заказов"]
end
Header --> Filters --> Table`,

View File

@@ -1,15 +1,10 @@
import DefaultTheme from 'vitepress/theme';
import type { Theme } from 'vitepress';
import MermaidDiagram from './components/MermaidDiagram.vue';
import NamedMermaidDiagram from './components/NamedMermaidDiagram.vue';
const theme: Theme = {
...DefaultTheme,
enhanceApp({ app }) {
DefaultTheme.enhanceApp?.({ app });
app.component('MermaidDiagram', MermaidDiagram);
app.component('NamedMermaidDiagram', NamedMermaidDiagram);
enhanceApp(ctx) {
DefaultTheme.enhanceApp?.(ctx);
},
};