mirror of
https://github.com/LibreChat-AI/librechat.ai.git
synced 2026-03-27 02:38:32 +07:00
* chore: upgrade eslint to v9 * chore: update package dependencies in package.json and pnpm-lock.yaml - Added `minimatch` and `serialize-javascript` dependencies with updated versions. - Upgraded `ajv` to version 6.14.0. - Removed outdated dependencies from pnpm-lock.yaml for better package management. * feat: add Stripe logos to Companies section - Introduced new company entry for Stripe in the Companies component, including both light and dark logo variants. - Updated the Companies array to display 10 logos instead of 8. - Adjusted TypeScript environment reference to point to the development types directory.
240 lines
6.1 KiB
TypeScript
240 lines
6.1 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 { TrackedLink, TrackedAnchor } from '@/components/TrackedLink'
|
|
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="[&>svg]:size-6">{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 (
|
|
<TrackedLink
|
|
href={href}
|
|
title={title}
|
|
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">
|
|
→
|
|
</span>
|
|
)}
|
|
</TrackedLink>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="rounded-xl border border-fd-border bg-fd-card p-5" {...props}>
|
|
{content}
|
|
</div>
|
|
)
|
|
}
|
|
const CardsWithCard = Object.assign(CardsCompat, { 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}
|
|
/>
|
|
)
|
|
}
|
|
const FileTreeWithChildren = Object.assign(FileTreeCompat, { File: FileCompat, Folder })
|
|
|
|
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,
|
|
a: TrackedAnchor,
|
|
img: ImgCompat,
|
|
Callout: CalloutCompat,
|
|
Steps,
|
|
Step,
|
|
Tab: TabCompat,
|
|
Tabs: TabsCompat,
|
|
Cards: CardsWithCard,
|
|
Card: CardCompat,
|
|
FileTree: FileTreeWithChildren,
|
|
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>
|
|
),
|
|
}
|