mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Feat/mermaid blog (#40897)
This commit is contained in:
1 parent
9f33f2e2cf
commit
40c781ece3
21 files changed
+767
-13
No files matched your search
@@ -2568,6 +2568,116 @@ export const Index: Record<string, any> = {
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-demo": {
|
||||
name: "mermaid-demo",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-demo")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-demo.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-flowchart": {
|
||||
name: "mermaid-flowchart",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-flowchart")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-flowchart.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-er-diagram": {
|
||||
name: "mermaid-er-diagram",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-er-diagram")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-er-diagram.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-sequence-async": {
|
||||
name: "mermaid-sequence-async",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-sequence-async")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-sequence-async.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-sequence-sync": {
|
||||
name: "mermaid-sequence-sync",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-sequence-sync")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-sequence-sync.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-oauth-flow": {
|
||||
name: "mermaid-oauth-flow",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-oauth-flow")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-oauth-flow.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-flowchart-subgraph": {
|
||||
name: "mermaid-flowchart-subgraph",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-flowchart-subgraph")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-flowchart-subgraph.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-sequence-api": {
|
||||
name: "mermaid-sequence-api",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-sequence-api")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-sequence-api.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-er-simple": {
|
||||
name: "mermaid-er-simple",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-er-simple")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-er-simple.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"mermaid-basic": {
|
||||
name: "mermaid-basic",
|
||||
type: "components:example",
|
||||
registryDependencies: ["mermaid"],
|
||||
component: React.lazy(() => import("@/registry/default/example/mermaid-basic")),
|
||||
source: "",
|
||||
files: ["registry/default/example/mermaid-basic.tsx"],
|
||||
category: "undefined",
|
||||
subcategory: "undefined",
|
||||
chunks: []
|
||||
},
|
||||
"chart-area-axes": {
|
||||
name: "chart-area-axes",
|
||||
type: "components:block",
|
||||
|
||||
@@ -317,6 +317,12 @@ export const docsConfig: DocsConfig = {
|
||||
href: '/docs/components/label',
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: 'Mermaid',
|
||||
href: '/docs/components/mermaid',
|
||||
items: [],
|
||||
label: 'New',
|
||||
},
|
||||
{
|
||||
title: 'Menubar',
|
||||
href: '/docs/components/menubar',
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Mermaid
|
||||
description: Render diagrams from text using Mermaid syntax.
|
||||
component: true
|
||||
---
|
||||
|
||||
<ComponentPreview name="mermaid-basic" peekCode wide />
|
||||
|
||||
## Usage
|
||||
|
||||
### In MDX (Blog Posts)
|
||||
|
||||
The simplest way to add diagrams in blog posts is with a fenced code block using the `mermaid` language tag:
|
||||
|
||||
````mdx
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[User Request] --> B{Authenticated?}
|
||||
B -->|Yes| C[Process Request]
|
||||
B -->|No| D[Return 401]
|
||||
```
|
||||
````
|
||||
|
||||
This is automatically rendered as a diagram. No imports needed.
|
||||
|
||||
### As a Component
|
||||
|
||||
For more control, import and use the component directly:
|
||||
|
||||
```tsx
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export function MyDiagram() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
flowchart LR
|
||||
A[User Request] --> B{Authenticated?}
|
||||
B -->|Yes| C[Process Request]
|
||||
B -->|No| D[Return 401]
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
Use the component when you need to pass props like `className`, conditionally render, or integrate with React logic.
|
||||
|
||||
## Flowcharts
|
||||
|
||||
Use `flowchart` or `graph` to create flowcharts with nodes and edges. Supports decision diamonds, different node shapes, and directional layouts (TD, LR, etc).
|
||||
[View Mermaid flowchart docs](https://mermaid.js.org/syntax/flowchart.html)
|
||||
|
||||
A basic request flow showing conditional branching with a decision node (`{}`), demonstrating how to visualize auth checks and data flow.
|
||||
|
||||
<ComponentPreview name="mermaid-flowchart" />
|
||||
|
||||
### With Subgraphs
|
||||
|
||||
Group related nodes into labeled subgraphs to show system boundaries. Useful for depicting frontend/backend separation or microservice architecture.
|
||||
|
||||
<ComponentPreview name="mermaid-flowchart-subgraph" />
|
||||
|
||||
### Supabase Architecture
|
||||
|
||||
Shows how client requests flow through the API Gateway to core services (Auth, PostgREST, Realtime) which all connect to PostgreSQL.
|
||||
|
||||
<ComponentPreview name="mermaid-demo" />
|
||||
|
||||
### OAuth Authorization Flow
|
||||
|
||||
Illustrates the OAuth 2.0 authorization code flow, showing the handoff between a third-party app, your authorization endpoint, and Supabase Auth for token exchange.
|
||||
|
||||
<ComponentPreview name="mermaid-oauth-flow" />
|
||||
|
||||
## ER Diagrams
|
||||
|
||||
Use `erDiagram` to create entity-relationship diagrams showing database schemas. Great for documenting table relationships and foreign keys.
|
||||
[View Mermaid ER diagram docs](https://mermaid.js.org/syntax/entityRelationshipDiagram.html)
|
||||
|
||||
A simple e-commerce schema showing cardinality between customers, orders, products, and line items using crow's foot notation.
|
||||
|
||||
<ComponentPreview name="mermaid-er-simple" />
|
||||
|
||||
### With Attributes
|
||||
|
||||
Include column definitions with types and constraints (PK, FK) to create more detailed schema documentation.
|
||||
|
||||
<ComponentPreview name="mermaid-er-diagram" />
|
||||
|
||||
## Sequence Diagrams
|
||||
|
||||
Use `sequenceDiagram` to show interactions between participants over time. Ideal for documenting API flows, authentication handshakes, or any request/response patterns.
|
||||
[View Mermaid sequence diagram docs](https://mermaid.js.org/syntax/sequenceDiagram.html)
|
||||
|
||||
A typical authenticated API request showing JWT validation, database insert, and response flow between Client, API, Auth, and DB participants.
|
||||
|
||||
<ComponentPreview name="mermaid-sequence-api" />
|
||||
|
||||
### Async Streaming (FDW)
|
||||
|
||||
Shows how Foreign Data Wrappers can stream data asynchronously using bounded channels, with parallel execution indicated by the `par` block.
|
||||
|
||||
<ComponentPreview name="mermaid-sequence-async" />
|
||||
|
||||
### Sync Blocking (FDW)
|
||||
|
||||
Contrasts with async by showing traditional blocking FDW behavior where all rows are fetched and buffered before processing.
|
||||
|
||||
<ComponentPreview name="mermaid-sequence-sync" />
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Description |
|
||||
| ----------- | -------- | ------------------------------------ |
|
||||
| `chart` | `string` | The Mermaid diagram definition |
|
||||
| `className` | `string` | Optional CSS class for the container |
|
||||
|
||||
## Learn More
|
||||
|
||||
See the [Mermaid documentation](https://mermaid.js.org/intro/) for full syntax reference and additional diagram types.
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidBasic() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
flowchart TD
|
||||
A[Start] --> B{Is it working?}
|
||||
B -->|Yes| C[Great!]
|
||||
B -->|No| D[Debug]
|
||||
D --> B
|
||||
C --> E[End]
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidDemo() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
flowchart TB
|
||||
Client[Your Application]
|
||||
|
||||
|
||||
Gateway[API Gateway]
|
||||
Auth[Auth]
|
||||
API[PostgREST]
|
||||
Realtime[Realtime]
|
||||
PgBouncer[PgBouncer]
|
||||
Postgres[(PostgreSQL)]
|
||||
|
||||
Client --> Gateway
|
||||
Client --> PgBouncer
|
||||
Client <--> Realtime
|
||||
Gateway --> Auth
|
||||
Gateway --> API
|
||||
Auth --> Postgres
|
||||
API --> Postgres
|
||||
Realtime <--> Postgres
|
||||
PgBouncer --> Postgres
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidERDiagram() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
erDiagram
|
||||
USERS ||--o{ POSTS : creates
|
||||
USERS ||--o{ COMMENTS : writes
|
||||
POSTS ||--o{ COMMENTS : has
|
||||
USERS {
|
||||
uuid id PK
|
||||
string email
|
||||
timestamp created_at
|
||||
}
|
||||
POSTS {
|
||||
uuid id PK
|
||||
uuid user_id FK
|
||||
string title
|
||||
text content
|
||||
}
|
||||
COMMENTS {
|
||||
uuid id PK
|
||||
uuid user_id FK
|
||||
uuid post_id FK
|
||||
text content
|
||||
}
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidErSimple() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ LINE_ITEM : contains
|
||||
PRODUCT ||--o{ LINE_ITEM : "ordered in"
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidFlowchartSubgraph() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
flowchart LR
|
||||
subgraph Frontend
|
||||
A[React App] --> B[API Client]
|
||||
end
|
||||
|
||||
subgraph Backend
|
||||
C[Edge Functions] --> D[Database]
|
||||
C --> E[Storage]
|
||||
end
|
||||
|
||||
B --> C
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidFlowchart() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
flowchart TD
|
||||
A[Client Request] --> B{Auth Check}
|
||||
B -->|Valid| C[Query Postgres]
|
||||
B -->|Invalid| D[Return 401]
|
||||
C --> E[Apply RLS Policies]
|
||||
E --> F[Return Data]
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidOAuthFlow() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
flowchart TD
|
||||
A["Third-party App"]
|
||||
B["Your Authorization Endpoint"]
|
||||
C["Supabase Auth Server"]
|
||||
D["Third-party App"]
|
||||
E["Supabase Auth Server"]
|
||||
F["Third-party App can now access resources"]
|
||||
|
||||
A -- "(1) Authorization request" --> B
|
||||
B -- "(2) User authenticates & approves" --> C
|
||||
C -- "(3) Authorization code issued" --> D
|
||||
D -- "(4) Exchange code for access token" --> E
|
||||
E -- "(5) Access token + refresh token" --> F
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidSequenceApi() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant API
|
||||
participant Auth
|
||||
participant DB
|
||||
|
||||
Client->>API: POST /api/data
|
||||
API->>Auth: Validate JWT
|
||||
Auth-->>API: Token valid
|
||||
API->>DB: INSERT query
|
||||
DB-->>API: Success
|
||||
API-->>Client: 201 Created
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidSequenceAsync() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant P as Postgres
|
||||
participant R as Remote Source
|
||||
P->>R: Execute Query
|
||||
P->>P: Spawn async task (non-blocking)
|
||||
par Async Task
|
||||
R->>P:
|
||||
Note over R,P: Stream rows incrementally<br/>Send via bounded channel
|
||||
end
|
||||
P-->P: Read from channel (blocking_recv)
|
||||
P-->P: Convert & emit row immediately
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Mermaid } from 'ui'
|
||||
|
||||
export default function MermaidSequenceSync() {
|
||||
return (
|
||||
<Mermaid
|
||||
chart={`
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant P as Postgres
|
||||
participant R as Remote Source
|
||||
P->>R: Execute Query
|
||||
R->>P: Wait (blocking)
|
||||
Note over R,P: Fetch all rows (or fixed batch)
|
||||
P-->P: Buffer all rows in memory
|
||||
P-->P: Convert & emit rows in the batch
|
||||
`}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1397,4 +1397,64 @@ export const examples: Registry = [
|
||||
type: 'components:example',
|
||||
files: ['example/metric-card-with-icon-link-tooltip.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-demo',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-demo.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-flowchart',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-flowchart.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-er-diagram',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-er-diagram.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-sequence-async',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-sequence-async.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-sequence-sync',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-sequence-sync.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-oauth-flow',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-oauth-flow.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-flowchart-subgraph',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-flowchart-subgraph.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-sequence-api',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-sequence-api.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-er-simple',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-er-simple.tsx'],
|
||||
},
|
||||
{
|
||||
name: 'mermaid-basic',
|
||||
type: 'components:example',
|
||||
registryDependencies: ['mermaid'],
|
||||
files: ['example/mermaid-basic.tsx'],
|
||||
},
|
||||
]
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
CollapsibleTrigger_Shadcn_,
|
||||
Heading,
|
||||
Image,
|
||||
Mermaid,
|
||||
} from 'ui'
|
||||
import { Admonition } from 'ui-patterns/admonition'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
@@ -105,7 +106,12 @@ export default function mdxComponents(type?: 'blog' | 'lp' | undefined) {
|
||||
},
|
||||
pre: (props: any) => {
|
||||
if (props.className !== ignoreClass) {
|
||||
return <CodeBlock {...props.children.props} />
|
||||
const childProps = props.children?.props
|
||||
// Detect mermaid code blocks and render with Mermaid component
|
||||
if (childProps?.className === 'language-mermaid') {
|
||||
return <Mermaid chart={childProps.children} />
|
||||
}
|
||||
return <CodeBlock {...childProps} />
|
||||
} else {
|
||||
return <code {...props} />
|
||||
}
|
||||
@@ -146,6 +152,7 @@ export default function mdxComponents(type?: 'blog' | 'lp' | undefined) {
|
||||
<p className={cn('-mt-6 text-foreground-lighter text-lg', props.className)} {...props} />
|
||||
),
|
||||
Admonition,
|
||||
Mermaid,
|
||||
}
|
||||
|
||||
return components as any
|
||||
|
||||
@@ -81,7 +81,7 @@ export async function mdxSerialize(source: string, options?: { tocDepth?: number
|
||||
theme: codeHikeTheme,
|
||||
lineNumbers: true,
|
||||
showCopyButton: true,
|
||||
skipLanguages: [],
|
||||
skipLanguages: ['mermaid'],
|
||||
autoImport: false,
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export * from './src/components/Accordion'
|
||||
export * from './src/components/Collapsible'
|
||||
export * from './src/components/CodeBlock'
|
||||
export * from './src/components/SimpleCodeBlock'
|
||||
export * from './src/components/Mermaid'
|
||||
|
||||
// NAV
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"input-otp": "^1.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.436.0",
|
||||
"mermaid": "^11.12.1",
|
||||
"next-themes": "^0.3.0",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
"prop-types": "^15.7.2",
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import mermaid from 'mermaid'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { cn } from '../../lib/utils/cn'
|
||||
|
||||
// @mildtomato - 28/11/2025
|
||||
// Colors are hardcoded because Mermaid's parser doesn't support CSS variables
|
||||
const darkThemeVariables = {
|
||||
background: 'transparent',
|
||||
mainBkg: '#171717',
|
||||
primaryTextColor: '#ededed',
|
||||
secondaryTextColor: '#a0a0a0',
|
||||
tertiaryTextColor: '#ededed',
|
||||
textColor: '#ededed',
|
||||
primaryColor: '#3ecf8e',
|
||||
primaryBorderColor: '#3ecf8e',
|
||||
secondaryColor: '#9333ea',
|
||||
secondaryBorderColor: '#a855f7',
|
||||
tertiaryColor: '#262626',
|
||||
tertiaryBorderColor: '#404040',
|
||||
lineColor: '#525252',
|
||||
border1: '#404040',
|
||||
border2: '#525252',
|
||||
noteBkgColor: '#1a3a2a',
|
||||
noteTextColor: '#ededed',
|
||||
noteBorderColor: '#3ecf8e',
|
||||
actorBkg: '#171717',
|
||||
actorBorder: '#525252',
|
||||
actorTextColor: '#ededed',
|
||||
actorLineColor: '#525252',
|
||||
activationBkgColor: '#9333ea',
|
||||
activationBorderColor: '#a855f7',
|
||||
signalColor: '#ededed',
|
||||
signalTextColor: '#ededed',
|
||||
sequenceNumberColor: '#171717',
|
||||
nodeBkg: '#262626',
|
||||
nodeBorder: '#404040',
|
||||
clusterBkg: '#1a1a1a',
|
||||
clusterBorder: '#404040',
|
||||
defaultLinkColor: '#3ecf8e',
|
||||
edgeLabelBackground: '#171717',
|
||||
attributeBackgroundColorOdd: '#262626',
|
||||
attributeBackgroundColorEven: '#171717',
|
||||
rowOdd: '#262626',
|
||||
rowEven: '#171717',
|
||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
||||
fontSize: '14px',
|
||||
}
|
||||
|
||||
const lightThemeVariables = {
|
||||
background: 'transparent',
|
||||
mainBkg: '#ffffff',
|
||||
primaryTextColor: '#1c1c1c',
|
||||
secondaryTextColor: '#6b7280',
|
||||
tertiaryTextColor: '#1c1c1c',
|
||||
textColor: '#1c1c1c',
|
||||
primaryColor: '#3ecf8e',
|
||||
primaryBorderColor: '#3ecf8e',
|
||||
secondaryColor: '#9333ea',
|
||||
secondaryBorderColor: '#a855f7',
|
||||
tertiaryColor: '#f5f5f5',
|
||||
tertiaryBorderColor: '#e5e5e5',
|
||||
lineColor: '#d4d4d4',
|
||||
border1: '#e5e5e5',
|
||||
border2: '#d4d4d4',
|
||||
noteBkgColor: '#ecfdf5',
|
||||
noteTextColor: '#1c1c1c',
|
||||
noteBorderColor: '#3ecf8e',
|
||||
actorBkg: '#ffffff',
|
||||
actorBorder: '#d4d4d4',
|
||||
actorTextColor: '#1c1c1c',
|
||||
actorLineColor: '#d4d4d4',
|
||||
activationBkgColor: '#9333ea',
|
||||
activationBorderColor: '#a855f7',
|
||||
signalColor: '#1c1c1c',
|
||||
signalTextColor: '#1c1c1c',
|
||||
sequenceNumberColor: '#ffffff',
|
||||
nodeBkg: '#f5f5f5',
|
||||
nodeBorder: '#e5e5e5',
|
||||
clusterBkg: '#fafafa',
|
||||
clusterBorder: '#e5e5e5',
|
||||
defaultLinkColor: '#3ecf8e',
|
||||
edgeLabelBackground: '#ffffff',
|
||||
attributeBackgroundColorOdd: '#f5f5f5',
|
||||
attributeBackgroundColorEven: '#ffffff',
|
||||
rowOdd: '#f5f5f5',
|
||||
rowEven: '#ffffff',
|
||||
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
||||
fontSize: '13px',
|
||||
}
|
||||
|
||||
export interface MermaidProps {
|
||||
/** Mermaid diagram definition (e.g. flowchart, sequence, erDiagram) */
|
||||
chart: string
|
||||
/** Additional CSS classes for the container */
|
||||
className?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a Mermaid diagram from text.
|
||||
*
|
||||
* Supports flowcharts, sequence diagrams, ER diagrams, and other Mermaid syntax.
|
||||
* Automatically adapts to light/dark theme.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* <Mermaid chart={`
|
||||
* flowchart LR
|
||||
* A[Start] --> B[End]
|
||||
* `} />
|
||||
* ```
|
||||
*
|
||||
* @see https://mermaid.js.org/intro/
|
||||
*/
|
||||
export function Mermaid({ chart, className }: MermaidProps) {
|
||||
const [svg, setSvg] = useState<string>('')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const { resolvedTheme } = useTheme()
|
||||
|
||||
const isDark = resolvedTheme === 'dark'
|
||||
|
||||
useEffect(() => {
|
||||
if (!resolvedTheme) return
|
||||
|
||||
// Re-initialize mermaid with current theme
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
theme: 'base',
|
||||
themeVariables: isDark ? darkThemeVariables : lightThemeVariables,
|
||||
sequence: {
|
||||
useMaxWidth: false,
|
||||
actorMargin: 150,
|
||||
messageMargin: 60,
|
||||
noteMargin: 20,
|
||||
},
|
||||
flowchart: {
|
||||
useMaxWidth: false,
|
||||
},
|
||||
er: {
|
||||
useMaxWidth: false,
|
||||
},
|
||||
})
|
||||
|
||||
const renderChart = async () => {
|
||||
try {
|
||||
// Mermaid requires a unique ID per render to avoid DOM conflicts
|
||||
const id = `mermaid-${Math.random().toString(36).substring(2, 11)}`
|
||||
const { svg } = await mermaid.render(id, chart.trim())
|
||||
// Mermaid outputs <br> which isn't valid XML - fix for browser compatibility
|
||||
setSvg(svg.replace(/<br\s*>/gi, '<br/>'))
|
||||
setError(null)
|
||||
} catch (err) {
|
||||
// Invalid chart syntax throws - display error instead of crashing
|
||||
console.error('Mermaid rendering error:', err)
|
||||
setError(err instanceof Error ? err.message : 'Failed to render diagram')
|
||||
}
|
||||
}
|
||||
|
||||
renderChart()
|
||||
}, [chart, resolvedTheme])
|
||||
|
||||
if (!resolvedTheme) {
|
||||
return <div className={cn('my-6 rounded-lg bg-muted p-6 animate-pulse h-64', className)} />
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'my-4 p-4 bg-destructive-200 border border-destructive-400 rounded-md',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<p className="text-destructive-600 text-sm font-mono">Mermaid Error: {error}</p>
|
||||
<pre className="mt-2 text-xs text-foreground-lighter overflow-auto">{chart}</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<figure
|
||||
className={cn(
|
||||
'my-6 w-full flex justify-center rounded-lg border p-6',
|
||||
'bg-white border-[#e5e5e5] dark:bg-[#171717] dark:border-[#333]',
|
||||
'[&_svg]:h-auto [&_svg]:max-w-full',
|
||||
className
|
||||
)}
|
||||
dangerouslySetInnerHTML={{ __html: svg }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { Mermaid, type MermaidProps } from './Mermaid'
|
||||
|
||||
Generated
+50
-11
@@ -263,7 +263,7 @@ importers:
|
||||
version: 15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
|
||||
next-contentlayer2:
|
||||
specifier: 0.4.6
|
||||
version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
|
||||
version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
|
||||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -1409,7 +1409,7 @@ importers:
|
||||
version: 15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
|
||||
next-contentlayer2:
|
||||
specifier: 0.4.6
|
||||
version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
|
||||
version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
|
||||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -2313,6 +2313,9 @@ importers:
|
||||
lucide-react:
|
||||
specifier: ^0.436.0
|
||||
version: 0.436.0(react@18.3.1)
|
||||
mermaid:
|
||||
specifier: ^11.12.1
|
||||
version: 11.12.1(supports-color@8.1.1)
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
|
||||
@@ -4591,6 +4594,9 @@ packages:
|
||||
'@mermaid-js/parser@0.6.2':
|
||||
resolution: {integrity: sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==}
|
||||
|
||||
'@mermaid-js/parser@0.6.3':
|
||||
resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==}
|
||||
|
||||
'@mertasan/tailwindcss-variables@2.7.0':
|
||||
resolution: {integrity: sha512-rKPhxi/0r6XWP0+OjPmsfrloX/TtQmvONj2Pr3Nl8BNBznQVP3M9sphguDBUDC0AiKYx2xgup3XzAhlIDLPLIA==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
@@ -11154,6 +11160,9 @@ packages:
|
||||
dagre-d3-es@7.0.11:
|
||||
resolution: {integrity: sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==}
|
||||
|
||||
dagre-d3-es@7.0.13:
|
||||
resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==}
|
||||
|
||||
damerau-levenshtein@1.0.8:
|
||||
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
|
||||
|
||||
@@ -11504,9 +11513,6 @@ packages:
|
||||
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
dompurify@3.2.4:
|
||||
resolution: {integrity: sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==}
|
||||
|
||||
dompurify@3.2.7:
|
||||
resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
|
||||
|
||||
@@ -14469,6 +14475,9 @@ packages:
|
||||
mermaid@11.12.0:
|
||||
resolution: {integrity: sha512-ZudVx73BwrMJfCFmSSJT84y6u5brEoV8DOItdHomNLz32uBjNrelm7mg95X7g+C6UoQH/W6mBLGDEDv73JdxBg==}
|
||||
|
||||
mermaid@11.12.1:
|
||||
resolution: {integrity: sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g==}
|
||||
|
||||
meros@1.3.0:
|
||||
resolution: {integrity: sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==}
|
||||
engines: {node: '>=13'}
|
||||
@@ -22672,6 +22681,10 @@ snapshots:
|
||||
dependencies:
|
||||
langium: 3.3.1
|
||||
|
||||
'@mermaid-js/parser@0.6.3':
|
||||
dependencies:
|
||||
langium: 3.3.1
|
||||
|
||||
'@mertasan/tailwindcss-variables@2.7.0(autoprefixer@10.4.21(postcss@8.5.6))(postcss@8.5.6)':
|
||||
dependencies:
|
||||
autoprefixer: 10.4.21(postcss@8.5.6)
|
||||
@@ -30771,6 +30784,11 @@ snapshots:
|
||||
d3: 7.9.0
|
||||
lodash-es: 4.17.21
|
||||
|
||||
dagre-d3-es@7.0.13:
|
||||
dependencies:
|
||||
d3: 7.9.0
|
||||
lodash-es: 4.17.21
|
||||
|
||||
damerau-levenshtein@1.0.8: {}
|
||||
|
||||
dat.gui@0.7.9: {}
|
||||
@@ -31040,10 +31058,6 @@ snapshots:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
|
||||
dompurify@3.2.4:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
|
||||
dompurify@3.2.7:
|
||||
optionalDependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
@@ -34662,6 +34676,31 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
mermaid@11.12.1(supports-color@8.1.1):
|
||||
dependencies:
|
||||
'@braintree/sanitize-url': 7.1.1
|
||||
'@iconify/utils': 3.0.2(supports-color@8.1.1)
|
||||
'@mermaid-js/parser': 0.6.3
|
||||
'@types/d3': 7.4.3
|
||||
cytoscape: 3.33.1
|
||||
cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1)
|
||||
cytoscape-fcose: 2.2.0(cytoscape@3.33.1)
|
||||
d3: 7.9.0
|
||||
d3-sankey: 0.12.3
|
||||
dagre-d3-es: 7.0.13
|
||||
dayjs: 1.11.18
|
||||
dompurify: 3.2.7
|
||||
katex: 0.16.22
|
||||
khroma: 2.1.0
|
||||
lodash-es: 4.17.21
|
||||
marked: 16.3.0
|
||||
roughjs: 4.6.6
|
||||
stylis: 4.3.6
|
||||
ts-dedent: 2.2.0
|
||||
uuid: 11.1.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
meros@1.3.0(@types/node@22.13.14):
|
||||
optionalDependencies:
|
||||
'@types/node': 22.13.14
|
||||
@@ -35469,7 +35508,7 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1):
|
||||
next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1):
|
||||
dependencies:
|
||||
'@contentlayer2/core': 0.4.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1)
|
||||
'@contentlayer2/utils': 0.4.3
|
||||
@@ -37764,7 +37803,7 @@ snapshots:
|
||||
classnames: 2.5.1
|
||||
core-js: 3.35.0
|
||||
decko: 1.2.0
|
||||
dompurify: 3.2.4
|
||||
dompurify: 3.2.7
|
||||
eventemitter3: 5.0.1
|
||||
json-pointer: 0.6.2
|
||||
lunr: 2.3.9
|
||||
|
||||
Reference in new issue
Block a user