Files
supabase/apps/ui-library/components/mdx-components.tsx
Tiago Antunes b79a64e301 feat: add Realtime Flow component (#44273)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Feature, docs update

## What is the new behavior?

This PR introduces a new `RealtimeFlow` component and hook to the UI
library for building collaborative React Flow with Supabase Realtime:
- keeps nodes and edges in sync across multiple connected clients in
real time
- uses Yjs with `@supabase-labs/y-supabase` to propagate flow updates
- supports optional persistence, so a flow can be restored from
previously saved shared state

## Additional context


https://github.com/user-attachments/assets/90d3a381-6f9c-427f-a493-5d91c2141462



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Collaborative "Realtime Flow" diagram editor with syncing overlays and
a dual-view demo component
* Interactive demo page and registry example for live editing
(add/remove/rename nodes)
* Framework-ready registry packages for Next.js, React, React Router,
and TanStack

* **Documentation**
* Comprehensive docs added for Next.js, React, React Router, and
TanStack (usage, persistence, hook API)

* **Chores**
  * Added runtime dependency for the flow component package

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/44273)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-26 13:28:52 +03:00

198 lines
6.5 KiB
TypeScript

import { useMDXComponent } from 'next-contentlayer2/hooks'
import Link from 'next/link'
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, cn } from 'ui'
import { BlockItem } from './block-item'
import { BlockPreview } from './block-preview'
import { Callout } from './callout'
import { ComponentPreview } from './component-preview'
import { CopyButton } from './copy-button'
import { DualRealtimeChat } from './dual-realtime-chat'
import { DualRealtimeFlow } from './dual-realtime-flow'
import { DualRealtimeMonaco } from './dual-realtime-monaco'
import { RegistryBlock } from './registry-block'
import { StyleWrapper } from './style-wrapper'
import TanStackBeta from './tanstack-beta'
import { TanstackDBGenerator } from './tanstack-db-generator'
import type { Style } from '@/registry/styles'
const components = {
RegistryBlock,
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
h1: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h1 className={cn('font-heading mt-2 scroll-m-20 text-4xl font-bold', className)} {...props} />
),
h2: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h2
className={cn(
'font-heading mt-12 scroll-m-20 border-b pb-2 text-2xl tracking-tight first:mt-0',
className
)}
{...props}
/>
),
h3: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h3
className={cn('font-heading mt-8 scroll-m-20 text-xl tracking-tight', className)}
{...props}
/>
),
h4: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h4
className={cn('font-heading mt-8 scroll-m-20 text-lg tracking-tight', className)}
{...props}
/>
),
h5: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h5 className={cn('mt-8 scroll-m-20 text-lg tracking-tight', className)} {...props} />
),
h6: ({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) => (
<h6 className={cn('mt-8 scroll-m-20 text-base tracking-tight', className)} {...props} />
),
a: ({ className, ...props }: React.HTMLAttributes<HTMLAnchorElement>) => (
<a
className={cn(
'text-foreground underline decoration-1 decoration-foreground-muted underline-offset-4 transition-colors hover:decoration-brand hover:decoration-2',
className
)}
{...props}
/>
),
p: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => (
<p className={cn('leading-7 not-first:mt-6 text-foreground-light', className)} {...props} />
),
ul: ({ className, ...props }: React.HTMLAttributes<HTMLUListElement>) => (
<ul className={cn('my-6 ml-6 list-disc text-foreground-light', className)} {...props} />
),
ol: ({ className, ...props }: React.HTMLAttributes<HTMLOListElement>) => (
<ol className={cn('my-6 ml-6 list-decimal text-foreground-light', className)} {...props} />
),
li: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
<li className={cn('mt-2', className)} {...props} />
),
blockquote: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
<blockquote className={cn('mt-6 border-l-2 pl-6 italic', className)} {...props} />
),
img: ({ className, alt, ...props }: React.ImgHTMLAttributes<HTMLImageElement>) => (
// eslint-disable-next-line @next/next/no-img-element
<img className={cn('rounded-md', className)} alt={alt} {...props} />
),
hr: ({ ...props }: React.HTMLAttributes<HTMLHRElement>) => (
<hr className="my-4 md:my-8" {...props} />
),
table: ({ className, ...props }: React.HTMLAttributes<HTMLTableElement>) => (
<div className="my-6 w-full overflow-y-auto">
<table className={cn('w-full', className)} {...props} />
</div>
),
tr: ({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) => (
<tr className={cn('m-0 border-t p-0 even:bg-surface-75/75', className)} {...props} />
),
th: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
<th
className={cn(
'border px-4 py-2 text-left font-normal [[align=center]]:text-center [[align=right]]:text-right',
className
)}
{...props}
/>
),
td: ({ className, ...props }: React.HTMLAttributes<HTMLTableCellElement>) => (
<td
className={cn(
'border text-foreground-light px-4 py-2 text-left [[align=center]]:text-center [[align=right]]:text-right',
className
)}
{...props}
/>
),
pre: ({
className,
__rawString__,
__withMeta__,
__src__,
__style__,
...props
}: React.HTMLAttributes<HTMLPreElement> & {
__style__?: Style['name']
__rawString__?: string
__withMeta__?: boolean
__src__?: string
}) => {
return (
<StyleWrapper styleName={__style__}>
<pre
className={cn(
'mb-4 mt-6 max-h-[650px] overflow-x-auto rounded-lg border bg-surface-75/75 py-4 text-foreground-light',
className
)}
{...props}
/>
{__rawString__ && (
<CopyButton
value={__rawString__}
src={__src__}
className={cn('absolute right-4 top-4', __withMeta__ && 'top-16')}
/>
)}
</StyleWrapper>
)
},
code: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
<code
className={cn(
'relative rounded-sm bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm',
className
)}
{...props}
/>
),
Callout,
ComponentPreview,
CopyButton,
TanStackBeta,
Card: ({ className, ...props }: React.HTMLAttributes<HTMLElement>) => (
<div
className={cn(
'flex w-full flex-col items-center rounded-xl border bg-surface-100 text-card-background py-6 px-4 shadow-sm transition-colors hover:bg-muted/50 sm:p-10',
className
)}
{...props}
/>
),
LinkedCard: ({ className, ...props }: React.ComponentProps<typeof Link>) => (
<Link
className={cn(
'flex w-full flex-col items-center justify-center rounded-xl border bg-surface-100 text-card-background py-6 px-4 shadow-sm transition-colors hover:bg-muted/50 sm:p-10 h-52',
className
)}
{...props}
/>
),
BlockItem,
BlockPreview,
DualRealtimeChat,
DualRealtimeFlow,
DualRealtimeMonaco,
TanstackDBGenerator,
}
interface MdxProps {
code: string
}
export function Mdx({ code }: MdxProps) {
const Component = useMDXComponent(code, {
style: 'default',
})
return (
<div className="mdx">
<Component components={components} />
</div>
)
}