import type React from 'react' import type { Root } from 'fumadocs-core/page-tree' import { findNeighbour } from 'fumadocs-core/page-tree' import type { ApiPageProps } from 'fumadocs-openapi/ui' import { createAPIPage } from 'fumadocs-openapi/ui' import { Pre } from 'fumadocs-ui/components/codeblock' import defaultMdxComponents from 'fumadocs-ui/mdx' import { DocsBody, DocsPage, DocsTitle } from 'fumadocs-ui/page' import { notFound } from 'next/navigation' import { PageFooter } from '@/components/docs-layout/page-footer' import { PageNavigationArrows } from '@/components/docs-layout/page-navigation-arrows' import { LLMCopyButton } from '@/components/page-actions' import { PageTypeBadge } from '@/components/page-type-badge' import { StructuredData } from '@/components/structured-data' import { CodeBlock } from '@/components/ui/code-block' import { Heading } from '@/components/ui/heading' import { ResponseSection } from '@/components/ui/response-section' import { i18n } from '@/lib/i18n' import { getApiSpecContent, openapi } from '@/lib/openapi' import { type PageData, source } from '@/lib/source' import { DOCS_BASE_URL } from '@/lib/urls' const SUPPORTED_LANGUAGES: Set = new Set(i18n.languages) const BASE_URL = DOCS_BASE_URL const OG_LOCALE_MAP: Record = { en: 'en_US', es: 'es_ES', fr: 'fr_FR', de: 'de_DE', ja: 'ja_JP', zh: 'zh_CN', } function resolveLangAndSlug(params: { slug?: string[]; lang: string }) { const isValidLang = SUPPORTED_LANGUAGES.has(params.lang) const lang = isValidLang ? params.lang : 'en' const slug = isValidLang ? params.slug : [params.lang, ...(params.slug ?? [])] return { lang, slug } } const APIPage = createAPIPage(openapi, { playground: { enabled: false }, content: { renderOperationLayout: async (slots) => { return (
{slots.header} {slots.apiPlayground} {slots.authSchemes &&
{slots.authSchemes}
} {slots.parameters} {slots.body &&
{slots.body}
} {slots.responses} {slots.callbacks}
{slots.apiExample}
) }, }, }) export default async function Page(props: { params: Promise<{ slug?: string[]; lang: string }> }) { const params = await props.params const { lang, slug } = resolveLangAndSlug(params) const page = source.getPage(slug, lang) if (!page) notFound() const data = page.data as unknown as PageData & { _openapi?: { method?: string } getAPIPageProps?: () => ApiPageProps } const isOpenAPI = '_openapi' in data && data._openapi != null const isApiReference = slug?.some((s) => s === 'api-reference') ?? false // Academy lessons are video-first: drop the "On this page" TOC and go full // width so the lesson hero/video gets the room (chapters live in-page instead). const isAcademy = slug?.[0] === 'academy' const pageTreeRecord = source.pageTree as Record const pageTree = pageTreeRecord[lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0] const rawNeighbours = pageTree ? findNeighbour(pageTree, page.url) : null // Academy and API Reference are self-contained sections; keep prev/next inside // the section instead of spilling into the main documentation tree. Match both // the section's pages (`//...`) and its index (`/`). const sectionSlug = isApiReference ? 'api-reference' : isAcademy ? 'academy' : null const inSection = (url?: string) => url != null && (url.includes(`/${sectionSlug}/`) || url.endsWith(`/${sectionSlug}`)) const neighbours = sectionSlug ? { previous: inSection(rawNeighbours?.previous?.url) ? rawNeighbours?.previous : undefined, next: inSection(rawNeighbours?.next?.url) ? rawNeighbours?.next : undefined, } : rawNeighbours const generateBreadcrumbs = () => { const breadcrumbs: Array<{ name: string; url: string }> = [ { name: 'Home', url: BASE_URL, }, ] const urlParts = page.url.split('/').filter(Boolean) let currentPath = '' urlParts.forEach((part, index) => { if (index === 0 && SUPPORTED_LANGUAGES.has(part)) { currentPath = `/${part}` return } currentPath += `/${part}` const name = part .split('-') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' ') if (index === urlParts.length - 1) { breadcrumbs.push({ name: data.title, url: `${BASE_URL}${page.url}`, }) } else { breadcrumbs.push({ name: name, url: `${BASE_URL}${currentPath}`, }) } }) return breadcrumbs } const breadcrumbs = generateBreadcrumbs() const footer = if (isOpenAPI && data.getAPIPageProps) { const apiProps = data.getAPIPageProps() const apiPageContent = getApiSpecContent( data.title, data.description, apiProps.operations ?? [] ) return ( <>
{data.title}
) } const MDX = data.body const markdownContent = await data.getText('processed') return ( <>
{data.pageType && } {data.title}
) => (
{props.children}
), h1: (props: React.HTMLAttributes) => ( ), h2: (props: React.HTMLAttributes) => ( ), h3: (props: React.HTMLAttributes) => ( ), h4: (props: React.HTMLAttributes) => ( ), h5: (props: React.HTMLAttributes) => ( ), h6: (props: React.HTMLAttributes) => ( ), }} />
) } export async function generateStaticParams() { return source.generateParams() } export async function generateMetadata(props: { params: Promise<{ slug?: string[]; lang: string }> }) { const params = await props.params const { lang, slug } = resolveLangAndSlug(params) const page = source.getPage(slug, lang) if (!page) notFound() const data = page.data as unknown as PageData const fullUrl = `${BASE_URL}${page.url}` const ogImageUrl = `${BASE_URL}/api/og?title=${encodeURIComponent(data.title)}` return { title: data.title, description: data.description || 'Documentation for Sim — the open-source AI workspace where teams build, deploy, and manage AI agents.', keywords: [ 'AI agents', 'AI workspace', 'AI agent builder', 'build AI agents', 'LLM orchestration', 'AI automation', 'knowledge base', 'AI integrations', data.title?.toLowerCase().split(' '), ] .flat() .filter(Boolean), authors: [{ name: 'Sim Team' }], category: 'Developer Tools', openGraph: { title: data.title, description: data.description || 'Documentation for Sim — the open-source AI workspace where teams build, deploy, and manage AI agents.', url: fullUrl, siteName: 'Sim Documentation', type: 'article', locale: OG_LOCALE_MAP[lang] ?? 'en_US', alternateLocale: i18n.languages.reduce((locales, l) => { if (l !== lang) { locales.push(OG_LOCALE_MAP[l] ?? 'en_US') } return locales }, []), images: [ { url: ogImageUrl, width: 1200, height: 675, alt: data.title, }, ], }, twitter: { card: 'summary_large_image', title: data.title, description: data.description || 'Documentation for Sim — the open-source AI workspace where teams build, deploy, and manage AI agents.', images: [ogImageUrl], creator: '@simdotai', site: '@simdotai', }, canonical: fullUrl, alternates: { canonical: fullUrl, languages: { 'x-default': `${BASE_URL}${page.url.replace(`/${lang}`, '')}`, en: `${BASE_URL}${page.url.replace(`/${lang}`, '')}`, es: `${BASE_URL}/es${page.url.replace(`/${lang}`, '')}`, fr: `${BASE_URL}/fr${page.url.replace(`/${lang}`, '')}`, de: `${BASE_URL}/de${page.url.replace(`/${lang}`, '')}`, ja: `${BASE_URL}/ja${page.url.replace(`/${lang}`, '')}`, zh: `${BASE_URL}/zh${page.url.replace(`/${lang}`, '')}`, }, }, } }