diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx index 5dd543dcb..920a8a40e 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx @@ -5,6 +5,7 @@ import { ChevronLeft, ChevronRight } from 'lucide-react' import Link from 'next/link' import { notFound } from 'next/navigation' import { StructuredData } from '@/components/structured-data' +import { CodeBlock } from '@/components/ui/code-block' import { source } from '@/lib/source' export const dynamic = 'force-dynamic' @@ -22,31 +23,143 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l pageTreeRecord[params.lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0] const neighbours = pageTree ? findNeighbour(pageTree, page.url) : null - const CustomFooter = () => ( -
- {neighbours?.previous ? ( - - - {neighbours.previous.name} - - ) : ( -
- )} + const generateBreadcrumbs = () => { + const breadcrumbs: Array<{ name: string; url: string }> = [ + { + name: 'Home', + url: baseUrl, + }, + ] - {neighbours?.next ? ( + const urlParts = page.url.split('/').filter(Boolean) + let currentPath = '' + + urlParts.forEach((part, index) => { + if (index === 0 && ['en', 'es', 'fr', 'de', 'ja', 'zh'].includes(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: page.data.title, + url: `${baseUrl}${page.url}`, + }) + } else { + breadcrumbs.push({ + name: name, + url: `${baseUrl}${currentPath}`, + }) + } + }) + + return breadcrumbs + } + + const breadcrumbs = generateBreadcrumbs() + + const CustomFooter = () => ( +
+ {/* Navigation links */} +
+ {neighbours?.previous ? ( + + + {neighbours.previous.name} + + ) : ( +
+ )} + + {neighbours?.next ? ( + + {neighbours.next.name} + + + ) : ( +
+ )} +
+ + {/* Divider line */} +
+ + {/* Social icons */} +
- {neighbours.next.name} - +
- ) : ( -
- )} + +
+ + +
+ +
) @@ -57,6 +170,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l description={page.data.description || ''} url={`${baseUrl}${page.url}`} lang={params.lang} + breadcrumb={breadcrumbs} /> {page.data.title} {page.data.description} - + @@ -128,8 +247,10 @@ export async function generateMetadata(props: { url: fullUrl, siteName: 'Sim Documentation', type: 'article', - locale: params.lang, - alternateLocale: ['en', 'fr', 'zh'].filter((lang) => lang !== params.lang), + locale: params.lang === 'en' ? 'en_US' : `${params.lang}_${params.lang.toUpperCase()}`, + alternateLocale: ['en', 'es', 'fr', 'de', 'ja', 'zh'] + .filter((lang) => lang !== params.lang) + .map((lang) => (lang === 'en' ? 'en_US' : `${lang}_${lang.toUpperCase()}`)), }, twitter: { card: 'summary', @@ -152,8 +273,12 @@ export async function generateMetadata(props: { alternates: { canonical: fullUrl, languages: { - en: `${baseUrl}/en${page.url.replace(`/${params.lang}`, '')}`, + 'x-default': `${baseUrl}${page.url.replace(`/${params.lang}`, '')}`, + en: `${baseUrl}${page.url.replace(`/${params.lang}`, '')}`, + es: `${baseUrl}/es${page.url.replace(`/${params.lang}`, '')}`, fr: `${baseUrl}/fr${page.url.replace(`/${params.lang}`, '')}`, + de: `${baseUrl}/de${page.url.replace(`/${params.lang}`, '')}`, + ja: `${baseUrl}/ja${page.url.replace(`/${params.lang}`, '')}`, zh: `${baseUrl}/zh${page.url.replace(`/${params.lang}`, '')}`, }, }, diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx index 7641682aa..5e9216796 100644 --- a/apps/docs/app/[lang]/layout.tsx +++ b/apps/docs/app/[lang]/layout.tsx @@ -2,11 +2,14 @@ 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 { ExternalLink, GithubIcon } from 'lucide-react' -import { Inter } from 'next/font/google' +import { Geist_Mono, Inter } from 'next/font/google' import Image from 'next/image' -import Link from 'next/link' -import { LanguageDropdown } from '@/components/ui/language-dropdown' +import { + SidebarFolder, + SidebarItem, + SidebarSeparator, +} from '@/components/docs-layout/sidebar-components' +import { Navbar } from '@/components/navbar/navbar' import { i18n } from '@/lib/i18n' import { source } from '@/lib/source' import '../global.css' @@ -14,6 +17,12 @@ import { Analytics } from '@vercel/analytics/next' const inter = Inter({ subsets: ['latin'], + variable: '--font-geist-sans', +}) + +const geistMono = Geist_Mono({ + subsets: ['latin'], + variable: '--font-geist-mono', }) const { provider } = defineI18nUI(i18n, { @@ -27,25 +36,18 @@ const { provider } = defineI18nUI(i18n, { fr: { displayName: 'Français', }, + de: { + displayName: 'Deutsch', + }, + ja: { + displayName: '日本語', + }, zh: { displayName: '简体中文', }, }, }) -const GitHubLink = () => ( -
- - - -
-) - type LayoutProps = { children: ReactNode params: Promise<{ lang: string }> @@ -54,43 +56,82 @@ type LayoutProps = { export default async function Layout({ children, params }: LayoutProps) { const { lang } = await params + const structuredData = { + '@context': 'https://schema.org', + '@type': 'WebSite', + name: 'Sim Documentation', + description: + 'Comprehensive documentation for Sim - the visual workflow builder for AI Agent Workflows.', + url: 'https://docs.sim.ai', + publisher: { + '@type': 'Organization', + name: 'Sim', + url: 'https://sim.ai', + logo: { + '@type': 'ImageObject', + url: 'https://docs.sim.ai/static/logo.png', + }, + }, + inLanguage: lang, + potentialAction: { + '@type': 'SearchAction', + target: { + '@type': 'EntryPoint', + urlTemplate: 'https://docs.sim.ai/api/search?q={search_term_string}', + }, + 'query-input': 'required name=search_term_string', + }, + } + return ( - - + + +