/** * Drop-in replacements for the Mintlify MDX components used across the docs, so the * content could be ported without rewriting pages. Styling lives in `app/global.css` * under the `pg-*` class names. */ import { Children, isValidElement, type ComponentProps, type ReactElement, type ReactNode, } from 'react'; import Link from 'fumadocs-core/link'; import { Tab as FumaTab, Tabs as FumaTabs } from 'fumadocs-ui/components/tabs'; import { ArrowRight, ChevronRight, CircleAlert, CircleCheck, Info as InfoIcon, Lightbulb, OctagonAlert, StickyNote, } from 'lucide-react'; import { cn } from '@/lib/cn'; import { Icon } from './icons'; /* -------------------------------------------------------------------------- */ /* Callouts */ /* -------------------------------------------------------------------------- */ type CalloutType = 'note' | 'info' | 'tip' | 'warning' | 'check' | 'danger'; const calloutIcons: Record = { note: StickyNote, info: InfoIcon, tip: Lightbulb, warning: CircleAlert, check: CircleCheck, danger: OctagonAlert, }; function Callout({ type, title, icon, children, }: { type: CalloutType; title?: ReactNode; icon?: string; children?: ReactNode; }) { const DefaultIcon = calloutIcons[type]; return (
{icon ? : }
{title &&

{title}

} {children}
); } type CalloutProps = { title?: ReactNode; icon?: string; children?: ReactNode }; export const Note = (p: CalloutProps) => ; export const Info = (p: CalloutProps) => ; export const Tip = (p: CalloutProps) => ; export const Warning = (p: CalloutProps) => ; export const Check = (p: CalloutProps) => ; export const Danger = (p: CalloutProps) => ; /* -------------------------------------------------------------------------- */ /* Cards */ /* -------------------------------------------------------------------------- */ export function Card({ title, icon, href, arrow, cta, horizontal, img, children, }: { title?: ReactNode; icon?: string; href?: string; arrow?: boolean | string; cta?: string; horizontal?: boolean; img?: string; children?: ReactNode; }) { const external = href ? /^https?:\/\//.test(href) : false; const showArrow = arrow === true || arrow === 'true' || (arrow === undefined && external); const content = ( <> {img && }
{icon && ( )}
{title && (

{title} {showArrow && }

)} {children &&
{children}
} {cta && (

{cta}

)}
); if (!href) return
{content}
; // A full-card overlay link instead of wrapping the card in : card bodies can contain // their own links, and nested elements are invalid HTML (hydration errors). return (
{content}
); } export function CardGroup({ cols = 2, children }: { cols?: number; children?: ReactNode }) { return (
{children}
); } export const Columns = CardGroup; /* -------------------------------------------------------------------------- */ /* Steps */ /* -------------------------------------------------------------------------- */ export function Steps({ children }: { children?: ReactNode }) { return
{children}
; } export function Step({ title, icon, children, }: { title?: ReactNode; icon?: string; stepNumber?: number; titleSize?: string; children?: ReactNode; }) { return (
{icon ? : null} {title &&

{title}

}
{children}
); } /* -------------------------------------------------------------------------- */ /* Tabs */ /* -------------------------------------------------------------------------- */ type TabElement = ReactElement<{ title?: string; children?: ReactNode }>; export function Tabs({ children }: { children?: ReactNode }) { const tabs = Children.toArray(children).filter( (child): child is TabElement => isValidElement(child), ); const items = tabs.map((tab, i) => tab.props.title ?? `Tab ${i + 1}`); return ( {tabs.map((tab, i) => ( {tab.props.children} ))} ); } /** Only rendered through , which reads its props directly. */ export function Tab({ children }: { title?: string; children?: ReactNode }) { return <>{children}; } /* -------------------------------------------------------------------------- */ /* Accordions & Expandables */ /* -------------------------------------------------------------------------- */ function slugify(value: unknown) { return typeof value === 'string' ? value .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)/g, '') : undefined; } export function Accordion({ title, description, icon, defaultOpen, children, }: { title: ReactNode; description?: ReactNode; icon?: string; defaultOpen?: boolean; children?: ReactNode; }) { return (
{icon && } {title} {description && ( {description} )}
{children}
); } export function AccordionGroup({ children }: { children?: ReactNode }) { return
{children}
; } export function Expandable({ title = 'properties', defaultOpen, children, }: { title?: string; defaultOpen?: boolean; children?: ReactNode; }) { return (
Show {title} Hide {title}
{children}
); } /* -------------------------------------------------------------------------- */ /* API fields */ /* -------------------------------------------------------------------------- */ export function ResponseField({ name, type, required, default: defaultValue, deprecated, post, pre, children, }: { name: string; type?: string; required?: boolean; default?: unknown; deprecated?: boolean; post?: string[]; pre?: string[]; children?: ReactNode; }) { const id = slugify(name); return (
{pre?.map((p) => ( {p} ))} {name} {type && {type}} {defaultValue !== undefined && ( default: {String(defaultValue)} )} {post?.map((p) => ( {p} ))} {required && required} {deprecated && deprecated}
{children &&
{children}
}
); } export const ParamField = ResponseField; /* -------------------------------------------------------------------------- */ /* Media */ /* -------------------------------------------------------------------------- */ export function Frame({ caption, hint, children, }: { caption?: ReactNode; hint?: ReactNode; children?: ReactNode; }) { return (
{hint &&

{hint}

} {children} {caption &&
{caption}
}
); } export function Iframe(props: ComponentProps<'iframe'>) { return (