chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
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<string> = new Set(i18n.languages)
|
||||
const BASE_URL = DOCS_BASE_URL
|
||||
|
||||
const OG_LOCALE_MAP: Record<string, string> = {
|
||||
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 (
|
||||
<div className='flex @4xl:flex-row flex-col @4xl:items-start gap-x-6 gap-y-4'>
|
||||
<div className='min-w-0 flex-1'>
|
||||
{slots.header}
|
||||
{slots.apiPlayground}
|
||||
{slots.authSchemes && <div className='api-section-divider'>{slots.authSchemes}</div>}
|
||||
{slots.parameters}
|
||||
{slots.body && <div className='api-section-divider'>{slots.body}</div>}
|
||||
<ResponseSection>{slots.responses}</ResponseSection>
|
||||
{slots.callbacks}
|
||||
</div>
|
||||
<div className='@4xl:sticky @4xl:top-[calc(var(--fd-docs-row-1,2rem)+1rem)] @4xl:w-[400px]'>
|
||||
{slots.apiExample}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
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<string, Root>
|
||||
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 (`/<slug>/...`) and its index (`/<slug>`).
|
||||
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 = <PageFooter previous={neighbours?.previous} next={neighbours?.next} />
|
||||
|
||||
if (isOpenAPI && data.getAPIPageProps) {
|
||||
const apiProps = data.getAPIPageProps()
|
||||
const apiPageContent = getApiSpecContent(
|
||||
data.title,
|
||||
data.description,
|
||||
apiProps.operations ?? []
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<StructuredData
|
||||
title={data.title}
|
||||
description={data.description || ''}
|
||||
url={`${BASE_URL}${page.url}`}
|
||||
lang={lang}
|
||||
breadcrumb={breadcrumbs}
|
||||
/>
|
||||
<DocsPage
|
||||
toc={data.toc}
|
||||
breadcrumb={{
|
||||
enabled: false,
|
||||
}}
|
||||
tableOfContent={{
|
||||
style: 'clerk',
|
||||
enabled: false,
|
||||
}}
|
||||
tableOfContentPopover={{
|
||||
style: 'clerk',
|
||||
enabled: false,
|
||||
}}
|
||||
footer={{
|
||||
enabled: true,
|
||||
component: footer,
|
||||
}}
|
||||
>
|
||||
<div className='api-page-header relative mt-6 sm:mt-0'>
|
||||
<div className='absolute top-1 right-0 flex items-center gap-2'>
|
||||
<div className='hidden sm:flex'>
|
||||
<LLMCopyButton content={apiPageContent} />
|
||||
</div>
|
||||
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
|
||||
</div>
|
||||
<DocsTitle className='mb-2'>{data.title}</DocsTitle>
|
||||
</div>
|
||||
<DocsBody>
|
||||
<APIPage {...apiProps} />
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const MDX = data.body
|
||||
const markdownContent = await data.getText('processed')
|
||||
|
||||
return (
|
||||
<>
|
||||
<StructuredData
|
||||
title={data.title}
|
||||
description={data.description || ''}
|
||||
url={`${BASE_URL}${page.url}`}
|
||||
lang={lang}
|
||||
breadcrumb={breadcrumbs}
|
||||
/>
|
||||
<DocsPage
|
||||
toc={data.toc}
|
||||
full={data.full || isAcademy}
|
||||
breadcrumb={{
|
||||
enabled: false,
|
||||
}}
|
||||
tableOfContent={{
|
||||
style: 'clerk',
|
||||
enabled: !isAcademy,
|
||||
single: false,
|
||||
}}
|
||||
tableOfContentPopover={{
|
||||
style: 'clerk',
|
||||
enabled: !isAcademy,
|
||||
}}
|
||||
footer={{
|
||||
enabled: true,
|
||||
component: footer,
|
||||
}}
|
||||
>
|
||||
<div className='relative mt-6 sm:mt-0'>
|
||||
<div className='absolute top-1 right-0 flex items-center gap-2'>
|
||||
<div className='hidden sm:flex'>
|
||||
<LLMCopyButton content={markdownContent} />
|
||||
</div>
|
||||
<PageNavigationArrows previous={neighbours?.previous} next={neighbours?.next} />
|
||||
</div>
|
||||
{data.pageType && <PageTypeBadge type={data.pageType} className='mb-3' />}
|
||||
<DocsTitle className='mb-2'>{data.title}</DocsTitle>
|
||||
</div>
|
||||
<DocsBody>
|
||||
<MDX
|
||||
components={{
|
||||
...defaultMdxComponents,
|
||||
pre: (props: React.HTMLAttributes<HTMLPreElement>) => (
|
||||
<CodeBlock {...props}>
|
||||
<Pre>{props.children}</Pre>
|
||||
</CodeBlock>
|
||||
),
|
||||
h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h1' {...props} />
|
||||
),
|
||||
h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h2' {...props} />
|
||||
),
|
||||
h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h3' {...props} />
|
||||
),
|
||||
h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h4' {...props} />
|
||||
),
|
||||
h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h5' {...props} />
|
||||
),
|
||||
h6: (props: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<Heading as='h6' {...props} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
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<string[]>((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}`, '')}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { defineI18nUI } from 'fumadocs-ui/i18n'
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs'
|
||||
import { RootProvider } from 'fumadocs-ui/provider/next'
|
||||
import { Geist_Mono, Inter } from 'next/font/google'
|
||||
import { AskAI } from '@/components/ai/ask-ai'
|
||||
import {
|
||||
SidebarFolder,
|
||||
SidebarItem,
|
||||
SidebarSeparator,
|
||||
} from '@/components/docs-layout/sidebar-components'
|
||||
import { Footer } from '@/components/footer/footer'
|
||||
import { Navbar } from '@/components/navbar/navbar'
|
||||
import { SimWordmark } from '@/components/ui/sim-logo'
|
||||
import { i18n } from '@/lib/i18n'
|
||||
import { serializeJsonLd } from '@/lib/json-ld'
|
||||
import { source } from '@/lib/source'
|
||||
import { DOCS_BASE_URL } from '@/lib/urls'
|
||||
import { season } from '@/app/fonts/season'
|
||||
import '../global.css'
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-geist-sans',
|
||||
display: 'swap',
|
||||
})
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-geist-mono',
|
||||
display: 'swap',
|
||||
})
|
||||
|
||||
const { provider } = defineI18nUI(i18n, {
|
||||
translations: {
|
||||
en: {
|
||||
displayName: 'English',
|
||||
},
|
||||
es: {
|
||||
displayName: 'Español',
|
||||
},
|
||||
fr: {
|
||||
displayName: 'Français',
|
||||
},
|
||||
de: {
|
||||
displayName: 'Deutsch',
|
||||
},
|
||||
ja: {
|
||||
displayName: '日本語',
|
||||
},
|
||||
zh: {
|
||||
displayName: '简体中文',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
type LayoutProps = {
|
||||
children: ReactNode
|
||||
params: Promise<{ lang: string }>
|
||||
}
|
||||
|
||||
const SUPPORTED_LANGUAGES: Set<string> = new Set(i18n.languages)
|
||||
|
||||
export default async function Layout({ children, params }: LayoutProps) {
|
||||
const { lang: rawLang } = await params
|
||||
const lang = SUPPORTED_LANGUAGES.has(rawLang) ? rawLang : 'en'
|
||||
|
||||
const structuredData = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: 'Sim Documentation',
|
||||
description:
|
||||
'Documentation for Sim — the open-source AI workspace where teams build, deploy, and manage AI agents. Connect 1,000+ integrations and every major LLM.',
|
||||
url: DOCS_BASE_URL,
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'Sim',
|
||||
url: 'https://sim.ai',
|
||||
logo: {
|
||||
'@type': 'ImageObject',
|
||||
url: `${DOCS_BASE_URL}/static/logo.png`,
|
||||
},
|
||||
},
|
||||
inLanguage: lang,
|
||||
}
|
||||
|
||||
return (
|
||||
<html
|
||||
lang={lang}
|
||||
className={`${inter.variable} ${geistMono.variable} ${season.variable}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<script
|
||||
type='application/ld+json'
|
||||
dangerouslySetInnerHTML={{ __html: serializeJsonLd(structuredData) }}
|
||||
/>
|
||||
</head>
|
||||
<body className='flex min-h-screen flex-col font-sans'>
|
||||
<RootProvider i18n={provider(lang)}>
|
||||
<Navbar />
|
||||
<DocsLayout
|
||||
tree={source.pageTree[lang]}
|
||||
nav={{
|
||||
title: <SimWordmark className='h-[18px]' />,
|
||||
}}
|
||||
sidebar={{
|
||||
tabs: false,
|
||||
defaultOpenLevel: 0,
|
||||
collapsible: false,
|
||||
footer: null,
|
||||
banner: null,
|
||||
components: {
|
||||
Item: SidebarItem,
|
||||
Folder: SidebarFolder,
|
||||
Separator: SidebarSeparator,
|
||||
},
|
||||
}}
|
||||
containerProps={{
|
||||
className: '!pt-0',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
<Footer />
|
||||
<AskAI locale={lang} />
|
||||
</RootProvider>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ChipLink } from '@sim/emcn'
|
||||
import { DocsPage } from 'fumadocs-ui/page'
|
||||
|
||||
export const metadata = {
|
||||
title: 'Page Not Found',
|
||||
}
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<DocsPage>
|
||||
<div className='flex min-h-[70vh] flex-col items-center justify-center gap-4 text-center'>
|
||||
<h1 className='bg-gradient-to-b from-[var(--brand-accent)] to-[var(--brand-accent-hover)] bg-clip-text font-semibold text-8xl text-transparent'>
|
||||
404
|
||||
</h1>
|
||||
<h2 className='font-semibold text-2xl text-[var(--text-primary)]'>Page Not Found</h2>
|
||||
<p className='text-[var(--text-muted)]'>
|
||||
The page you're looking for doesn't exist or has been moved.
|
||||
</p>
|
||||
<ChipLink href='/' variant='primary'>
|
||||
Go home
|
||||
</ChipLink>
|
||||
</div>
|
||||
</DocsPage>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user