Files
librechat.ai/lib/mdx-components.tsx
Marco Beretta e3ad59744d feat: redesign docs site pages, migrate legal/about to App Router, add feedback widget (#502)
* feat: update table of content style in DocsPage component

* feat: add feedback widget, redesign DocsHub, and add sidebar logo

* feat: update demo images for dark and light themes

* feat: update demo images for dark and light themes

* feat: replace logo image and remove duplicate SVG file

* feat: add LocalInstallHub component and update documentation for local installation

* feat: enhance UI by updating feature icons and descriptions, and improving layout responsiveness

* Add legal pages: Cookie Policy, Privacy Policy, and Terms of Service

- Implemented Cookie Policy page with details on cookie usage and user privacy.
- Created Privacy Policy page outlining data collection practices and user rights.
- Developed Terms of Service page defining the usage terms for the documentation site.
- Removed outdated MDX files for cookie, privacy, and terms of service.
- Updated FeaturesHub component to include new feature highlights and improved layout.

* feat: enhance GitHub data fetching to include contributor count and update CommunitySection layout
2026-02-19 00:06:58 +01:00

239 lines
5.9 KiB
TypeScript

import defaultMdxComponents from 'fumadocs-ui/mdx'
import { Callout } from 'fumadocs-ui/components/callout'
import { Step, Steps } from 'fumadocs-ui/components/steps'
import { Tabs } from 'fumadocs-ui/components/tabs'
import { File as FumadocsFile, Folder, Files } from 'fumadocs-ui/components/files'
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion'
import { OptionTable } from '@/components/table'
import { Frame } from '@/components/Frame'
import { DocsHub } from '@/components/DocsHub'
import { LocalInstallHub } from '@/components/LocalInstallHub'
import { QuickStartHub } from '@/components/QuickStartHub'
import { FeaturesHub } from '@/components/FeaturesHub'
import Carousel from '@/components/carousel/Carousel'
import type { ReactNode } from 'react'
function mapCalloutType(type?: string): 'info' | 'warn' | 'error' {
switch (type) {
case 'warning':
case 'warn':
case 'important':
case 'danger':
return 'warn'
case 'error':
case 'bug':
return 'error'
default:
return 'info'
}
}
function CalloutCompat({
children,
type,
title,
collapsible,
...props
}: {
children?: ReactNode
type?: string
title?: ReactNode
collapsible?: boolean
[key: string]: any
}) {
const mappedType = mapCalloutType(type)
if (collapsible && title) {
return (
<Accordions type="single" collapsible>
<Accordion title={typeof title === 'string' ? title : 'Details'}>
<Callout type={mappedType} {...props}>
{children}
</Callout>
</Accordion>
</Accordions>
)
}
return (
<Callout type={mappedType} title={title} {...props}>
{children}
</Callout>
)
}
/**
* Simple Tab/Tabs wrappers that render content directly.
* The fumadocs-mdx compiler generates tab structures that may be incomplete
* when the remarkCodeTab plugin isn't available. These wrappers render the
* content as simple divs to avoid Radix UI context errors.
*/
function TabCompat({ children, ...props }: { children?: ReactNode; [key: string]: any }) {
return <div {...props}>{children}</div>
}
function TabsCompat({
children,
items,
...props
}: {
children?: ReactNode
items?: string[]
[key: string]: any
}) {
if (items && items.length > 0) {
// Use real Fumadocs Tabs when items are provided (explicit Tabs usage)
return (
<Tabs items={items} {...props}>
{children}
</Tabs>
)
}
// Fallback: render children directly for auto-generated tab structures
return <div {...props}>{children}</div>
}
;(TabsCompat as any).Tab = TabCompat
function CardsCompat({
children,
num,
...props
}: {
children?: ReactNode
num?: number
[key: string]: any
}) {
const gridClass =
num === 2
? 'grid grid-cols-1 gap-4 sm:grid-cols-2'
: num === 4
? 'grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'
: 'grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3'
return (
<div className={gridClass} {...props}>
{children}
</div>
)
}
function CardCompat({
children,
title,
href,
icon,
arrow,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
image,
...props
}: {
children?: ReactNode
title?: string
href?: string
icon?: ReactNode
arrow?: boolean
image?: boolean
[key: string]: any
}) {
const content = (
<div className="flex flex-col gap-2">
{icon && <div className="text-2xl">{icon}</div>}
{title && <h3 className="font-semibold text-fd-foreground">{title}</h3>}
{children && <div className="text-sm text-fd-muted-foreground">{children}</div>}
</div>
)
if (href) {
return (
<a
href={href}
className="group block rounded-xl border border-fd-border bg-fd-card p-5 transition-all hover:border-fd-primary/30 hover:bg-fd-accent hover:shadow-sm"
{...props}
>
{content}
{arrow && (
<span className="mt-3 inline-block text-sm text-fd-muted-foreground transition-transform group-hover:translate-x-0.5">
&rarr;
</span>
)}
</a>
)
}
return (
<div className="rounded-xl border border-fd-border bg-fd-card p-5" {...props}>
{content}
</div>
)
}
;(CardsCompat as any).Card = CardCompat
function FileTreeCompat({ children, ...props }: { children?: ReactNode; [key: string]: any }) {
return <Files {...props}>{children}</Files>
}
function FileCompat({
active,
className,
...props
}: {
active?: boolean
className?: string
name: string
icon?: ReactNode
[key: string]: any
}) {
return (
<FumadocsFile
className={
active ? `${className ?? ''} bg-fd-accent text-fd-accent-foreground`.trim() : className
}
{...props}
/>
)
}
;(FileTreeCompat as any).File = FileCompat
;(FileTreeCompat as any).Folder = Folder
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function ImgCompat({ image, ...props }: { image?: boolean; [key: string]: any }) {
const src = typeof props.src === 'string' ? props.src : ''
const isExternal = src.startsWith('http://') || src.startsWith('https://')
if (isExternal) {
// eslint-disable-next-line @next/next/no-img-element
return <img {...props} loading="lazy" />
}
const DefaultImg = defaultMdxComponents.img
if (DefaultImg) return <DefaultImg {...props} />
// eslint-disable-next-line @next/next/no-img-element
return <img {...props} />
}
export const mdxComponents = {
...defaultMdxComponents,
img: ImgCompat,
Callout: CalloutCompat,
Steps,
Step,
Tab: TabCompat,
Tabs: TabsCompat,
Cards: CardsCompat,
Card: CardCompat,
FileTree: FileTreeCompat,
File: FileCompat,
Folder,
Files,
Accordion,
Accordions,
OptionTable,
Frame,
Carousel,
DocsHub,
QuickStartHub,
FeaturesHub,
LocalInstallHub,
Button: ({ children, ...props }: { children?: ReactNode; [key: string]: any }) => (
<button className="rounded-md bg-fd-primary px-4 py-2 text-fd-primary-foreground" {...props}>
{children}
</button>
),
}