From fcc4fef2c3fb03a8389ffa704096dda2d7fd958a Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Fri, 28 Nov 2025 22:33:35 +0800 Subject: [PATCH] Add new Mermaid diagram examples and improve docs Introduces new example components for basic, subgraph flowchart, simple ER, and API sequence Mermaid diagrams. Updates the documentation to provide clearer usage instructions, more examples, and improved structure. Refactors the Mermaid component to enhance zoom functionality, removes custom SVG message backgrounds, and adds a dedicated CSS file for zoom overlay theming. --- apps/design-system/__registry__/index.tsx | 41 ++++- .../content/docs/components/mermaid.mdx | 140 +++++++++++------- .../default/example/mermaid-basic.tsx | 16 ++ .../registry/default/example/mermaid-demo.tsx | 56 +++---- .../default/example/mermaid-er-simple.tsx | 14 ++ .../example/mermaid-flowchart-subgraph.tsx | 21 +++ .../default/example/mermaid-sequence-api.tsx | 22 +++ apps/design-system/registry/examples.ts | 24 +++ .../ui/src/components/Mermaid/Mermaid.tsx | 68 +++------ .../src/components/Mermaid/mermaid-zoom.css | 13 ++ 10 files changed, 276 insertions(+), 139 deletions(-) create mode 100644 apps/design-system/registry/default/example/mermaid-basic.tsx create mode 100644 apps/design-system/registry/default/example/mermaid-er-simple.tsx create mode 100644 apps/design-system/registry/default/example/mermaid-flowchart-subgraph.tsx create mode 100644 apps/design-system/registry/default/example/mermaid-sequence-api.tsx create mode 100644 packages/ui/src/components/Mermaid/mermaid-zoom.css diff --git a/apps/design-system/__registry__/index.tsx b/apps/design-system/__registry__/index.tsx index e2bc55b003c..06f5d1ad19a 100644 --- a/apps/design-system/__registry__/index.tsx +++ b/apps/design-system/__registry__/index.tsx @@ -2601,13 +2601,46 @@ export const Index: Record = { subcategory: "undefined", chunks: [] }, - "mermaid-styling": { - name: "mermaid-styling", + "mermaid-flowchart-subgraph": { + name: "mermaid-flowchart-subgraph", type: "components:example", registryDependencies: ["mermaid"], - component: React.lazy(() => import("@/registry/default/example/mermaid-styling")), + component: React.lazy(() => import("@/registry/default/example/mermaid-flowchart-subgraph")), source: "", - files: ["registry/default/example/mermaid-styling.tsx"], + 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: [] diff --git a/apps/design-system/content/docs/components/mermaid.mdx b/apps/design-system/content/docs/components/mermaid.mdx index 22e49fe3ef9..3133c9504f8 100644 --- a/apps/design-system/content/docs/components/mermaid.mdx +++ b/apps/design-system/content/docs/components/mermaid.mdx @@ -4,85 +4,123 @@ description: Render diagrams from text using Mermaid syntax. component: true --- - - -## Installation - -The Mermaid component is available from the `ui` package: - -```tsx -import { Mermaid } from 'ui' -``` + ## 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 (custom className, disabling zoom), import and use the component directly: + ```tsx import { Mermaid } from 'ui' -``` -```tsx - B[End] +export function MyDiagram() { + return ( + B{Authenticated?} + B -->|Yes| C[Process Request] + B -->|No| D[Return 401] `} -/> + /> + ) +} ``` -## Examples +Use the component when you need to pass props like `className` or `zoomable`, conditionally render, or integrate with React logic. -### Supabase Architecture +## 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) ---- - -### Flowchart +A basic request flow showing conditional branching with a decision node (`{}`), demonstrating how to visualize auth checks and data flow. ---- +### With Subgraphs -### ER Diagram +Group related nodes into labeled subgraphs to show system boundaries. Useful for depicting frontend/backend separation or microservice architecture. - + ---- +### Supabase Architecture -### Async Streaming (FDW) +Shows how client requests flow through the API Gateway to core services (Auth, PostgREST, Realtime) which all connect to PostgreSQL. - - ---- - -### Sync Blocking (FDW) - - - ---- + ### 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. + +## 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. + + + +### With Attributes + +Include column definitions with types and constraints (PK, FK) to create more detailed schema documentation. + + + +## 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. + + + +### Async Streaming (FDW) + +Shows how Foreign Data Wrappers can stream data asynchronously using bounded channels, with parallel execution indicated by the `par` block. + + + +### Sync Blocking (FDW) + +Contrasts with async by showing traditional blocking FDW behavior where all rows are fetched and buffered before processing. + + + ## Props -| Prop | Type | Description | -| ----------- | -------- | ------------------------------------ | -| `chart` | `string` | The Mermaid diagram definition | -| `className` | `string` | Optional CSS class for the container | +| Prop | Type | Default | Description | +| ----------- | --------- | ------- | ------------------------------------ | +| `chart` | `string` | — | The Mermaid diagram definition | +| `className` | `string` | — | Optional CSS class for the container | +| `zoomable` | `boolean` | `true` | Enable click-to-zoom on the diagram | -## Supported Diagram Types +## Learn More -Mermaid supports many diagram types including: +See the [Mermaid documentation](https://mermaid.js.org/intro/) for full syntax reference and additional diagram types. -- **Flowcharts** - `flowchart` or `graph` -- **Sequence Diagrams** - `sequenceDiagram` -- **Class Diagrams** - `classDiagram` -- **State Diagrams** - `stateDiagram-v2` -- **ER Diagrams** - `erDiagram` -- **Gantt Charts** - `gantt` -- **Pie Charts** - `pie` -- **Git Graphs** - `gitGraph` +``` -See the [Mermaid documentation](https://mermaid.js.org/intro/) for full syntax reference. +``` diff --git a/apps/design-system/registry/default/example/mermaid-basic.tsx b/apps/design-system/registry/default/example/mermaid-basic.tsx new file mode 100644 index 00000000000..4fb5c6f2d6f --- /dev/null +++ b/apps/design-system/registry/default/example/mermaid-basic.tsx @@ -0,0 +1,16 @@ +import { Mermaid } from 'ui' + +export default function MermaidBasic() { + return ( + B{Is it working?} + B -->|Yes| C[Great!] + B -->|No| D[Debug] + D --> B + C --> E[End] +`} + /> + ) +} diff --git a/apps/design-system/registry/default/example/mermaid-demo.tsx b/apps/design-system/registry/default/example/mermaid-demo.tsx index dfa6e7bc69a..a282b9d2df9 100644 --- a/apps/design-system/registry/default/example/mermaid-demo.tsx +++ b/apps/design-system/registry/default/example/mermaid-demo.tsx @@ -5,45 +5,25 @@ export default function MermaidDemo() { Auth] - API[PostgREST
REST API] - Realtime[Realtime
WebSockets] - Storage[Storage API
S3-compatible] - Meta[postgres-meta
DB Management] - Functions[Deno
Edge Functions] - GraphQL[pg_graphql
GraphQL] - end - - subgraph Data - Postgres[(PostgreSQL)] - Supavisor[Supavisor
Connection Pooler] - end - - App --> Kong - Kong --> Auth - Kong --> API - Kong --> Realtime - Kong --> Storage - Kong --> Meta - Kong --> Functions - Kong --> GraphQL - - Auth --> Supavisor - API --> Supavisor - Realtime --> Supavisor - Storage --> Supavisor - Meta --> Supavisor - GraphQL --> Supavisor - Supavisor --> Postgres + Client --> Gateway + Client --> PgBouncer + Client <--> Realtime + Gateway --> Auth + Gateway --> API + Auth --> Postgres + API --> Postgres + Realtime <--> Postgres + PgBouncer --> Postgres `} /> ) diff --git a/apps/design-system/registry/default/example/mermaid-er-simple.tsx b/apps/design-system/registry/default/example/mermaid-er-simple.tsx new file mode 100644 index 00000000000..b49f8e366cf --- /dev/null +++ b/apps/design-system/registry/default/example/mermaid-er-simple.tsx @@ -0,0 +1,14 @@ +import { Mermaid } from 'ui' + +export default function MermaidErSimple() { + return ( + + ) +} diff --git a/apps/design-system/registry/default/example/mermaid-flowchart-subgraph.tsx b/apps/design-system/registry/default/example/mermaid-flowchart-subgraph.tsx new file mode 100644 index 00000000000..5ba8f754fec --- /dev/null +++ b/apps/design-system/registry/default/example/mermaid-flowchart-subgraph.tsx @@ -0,0 +1,21 @@ +import { Mermaid } from 'ui' + +export default function MermaidFlowchartSubgraph() { + return ( + B[API Client] + end + + subgraph Backend + C[Edge Functions] --> D[Database] + C --> E[Storage] + end + + B --> C +`} + /> + ) +} diff --git a/apps/design-system/registry/default/example/mermaid-sequence-api.tsx b/apps/design-system/registry/default/example/mermaid-sequence-api.tsx new file mode 100644 index 00000000000..69050f824dc --- /dev/null +++ b/apps/design-system/registry/default/example/mermaid-sequence-api.tsx @@ -0,0 +1,22 @@ +import { Mermaid } from 'ui' + +export default function MermaidSequenceApi() { + return ( + >API: POST /api/data + API->>Auth: Validate JWT + Auth-->>API: Token valid + API->>DB: INSERT query + DB-->>API: Success + API-->>Client: 201 Created +`} + /> + ) +} diff --git a/apps/design-system/registry/examples.ts b/apps/design-system/registry/examples.ts index 4ac8d96fa35..5a9f1fa4efa 100644 --- a/apps/design-system/registry/examples.ts +++ b/apps/design-system/registry/examples.ts @@ -1389,4 +1389,28 @@ export const examples: Registry = [ 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'], + }, ] diff --git a/packages/ui/src/components/Mermaid/Mermaid.tsx b/packages/ui/src/components/Mermaid/Mermaid.tsx index 81e29f2770a..c8875b13e4c 100644 --- a/packages/ui/src/components/Mermaid/Mermaid.tsx +++ b/packages/ui/src/components/Mermaid/Mermaid.tsx @@ -1,6 +1,7 @@ 'use client' import 'react-medium-image-zoom/dist/styles.css' +import './mermaid-zoom.css' import { useEffect, useRef, useState } from 'react' import mermaid from 'mermaid' @@ -42,7 +43,6 @@ const darkThemeVariables = { clusterBorder: '#404040', defaultLinkColor: '#3ecf8e', edgeLabelBackground: '#171717', - // ER diagram specific attributeBackgroundColorOdd: '#262626', attributeBackgroundColorEven: '#171717', rowOdd: '#262626', @@ -85,7 +85,6 @@ const lightThemeVariables = { clusterBorder: '#e5e5e5', defaultLinkColor: '#3ecf8e', edgeLabelBackground: '#ffffff', - // ER diagram specific attributeBackgroundColorOdd: '#f5f5f5', attributeBackgroundColorEven: '#ffffff', rowOdd: '#f5f5f5', @@ -129,6 +128,7 @@ export function Mermaid({ chart, className, zoomable = true }: MermaidProps) { }, flowchart: { useMaxWidth: false, + curve: 'linear', }, er: { useMaxWidth: false, @@ -197,36 +197,6 @@ export function Mermaid({ chart, className, zoomable = true }: MermaidProps) { rect.setAttribute('stroke', isPostgres ? '#336791' : isDark ? '#525252' : '#d4d4d4') }) - // Add pill backgrounds to message text (skip notes - they already have backgrounds) - const messageTexts = doc.querySelectorAll('.messageText') - messageTexts.forEach((text) => { - // Skip if there's already a sibling rect (notes have background rects) - const parent = text.parentElement - if (parent?.querySelector('rect')) return - - const x = parseFloat(text.getAttribute('x') || '0') - const y = parseFloat(text.getAttribute('y') || '0') - const dy = text.getAttribute('dy') - // Parse dy (e.g., "1em" -> 16px) - const dyOffset = dy ? parseFloat(dy) * 16 : 0 - - const textContent = text.textContent || '' - const estimatedWidth = textContent.length * 9 - const height = 28 - const paddingX = 16 - - const rect = doc.createElementNS('http://www.w3.org/2000/svg', 'rect') - rect.setAttribute('x', String(x - estimatedWidth / 2 - paddingX)) - rect.setAttribute('y', String(y + dyOffset - height / 2)) - rect.setAttribute('width', String(estimatedWidth + paddingX * 2)) - rect.setAttribute('height', String(height)) - rect.setAttribute('rx', '14') - rect.setAttribute('fill', isDark ? '#262626' : '#f5f5f5') - rect.setAttribute('class', 'messageTextBg') - - text.parentNode?.insertBefore(rect, text) - }) - const serializer = new XMLSerializer() const processedSvg = serializer.serializeToString(doc) setSvg(processedSvg) @@ -258,23 +228,29 @@ export function Mermaid({ chart, className, zoomable = true }: MermaidProps) { ) } - const content = ( -
+ const containerClassName = cn( + 'my-6 flex justify-center rounded-lg border p-6 pb-8', + isDark ? 'bg-[#171717] border-[#333]' : 'bg-white border-[#e5e5e5]', + className ) if (zoomable) { - return {content} + // Convert SVG to data URL for react-medium-image-zoom compatibility + const svgDataUrl = `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svg)))}` + return ( +
+ + Mermaid diagram + +
+ ) } - return content + return ( +
+ ) } diff --git a/packages/ui/src/components/Mermaid/mermaid-zoom.css b/packages/ui/src/components/Mermaid/mermaid-zoom.css new file mode 100644 index 00000000000..2a4907a660c --- /dev/null +++ b/packages/ui/src/components/Mermaid/mermaid-zoom.css @@ -0,0 +1,13 @@ +@tailwind utilities; + +@layer utilities { + [data-rmiz-modal-overlay='visible'] { + @apply !bg-white; + } + + .dark [data-rmiz-modal-overlay='visible'], + [data-theme*='dark'] [data-rmiz-modal-overlay='visible'] { + @apply !bg-[#171717]; + } +} +