diff --git a/.agents/skills/add-column-type/SKILL.md b/.agents/skills/add-column-type/SKILL.md index e9a11707e43..1c54b12cb3a 100644 --- a/.agents/skills/add-column-type/SKILL.md +++ b/.agents/skills/add-column-type/SKILL.md @@ -149,7 +149,7 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not, - [ ] `migrateCellsTo` / `migrateCellsFrom` added if the stored bytes change - [ ] New metadata keys added to `TYPE_SPECIFIC_COLUMN_KEYS` + `FOREIGN_METADATA_VERB` - [ ] Unit tests for `coerce` / `isCompatibleWith` round-trips, verified to fail without the code -- [ ] Docs row added to `apps/docs/content/docs/en/tables/index.mdx` +- [ ] Docs row added to `apps/docs/content/docs/tables/index.mdx` ## Final Validation (Required) diff --git a/.agents/skills/add-integration/SKILL.md b/.agents/skills/add-integration/SKILL.md index eea2d054ccf..d3d8e8f64ea 100644 --- a/.agents/skills/add-integration/SKILL.md +++ b/.agents/skills/add-integration/SKILL.md @@ -576,7 +576,7 @@ bun run deployment-config:check bun run docs:check ``` -This creates `apps/docs/content/docs/en/integrations/{service}.mdx` — one page per service carrying the block's Actions and, if it has one, its Triggers section. Never hand-edit generated pages; the only editable region is the `{/* MANUAL-CONTENT */}` block (see `scripts/README.md`). +This creates `apps/docs/content/docs/integrations/{service}.mdx` — one page per service carrying the block's Actions and, if it has one, its Triggers section. Never hand-edit generated pages; the only editable region is the `{/* MANUAL-CONTENT */}` block (see `scripts/README.md`). The docs generator refreshes `packages/deployment-config/src/integrations.json`, and the deployment config generator projects service-account provider IDs from that catalog plus the canonical OAuth diff --git a/.agents/skills/add-model/SKILL.md b/.agents/skills/add-model/SKILL.md index df17836a790..d418773ff2e 100644 --- a/.agents/skills/add-model/SKILL.md +++ b/.agents/skills/add-model/SKILL.md @@ -153,7 +153,7 @@ If the entry has `capabilities.thinking` or `capabilities.reasoningEffort`, it a - **Anthropic-family (`anthropic`, `azure-anthropic`) thinking models MUST declare `capabilities.thinking.streamed`** (`'full' | 'summary' | 'none'`). Verify against Anthropic's current thinking-display and streaming docs: visible thinking returned by the API is summarized, including when Sim opts models whose default display is `omitted` into `display: 'summarized'` on agent-events runs, so current Claude thinking models use `'summary'`. Use `'full'` only if future official API docs explicitly guarantee raw thinking deltas. `bun run agent-stream-docs:check` (CI) fails if the field is missing. - Other families usually omit the field and inherit the provider default in `getThinkingStreamVisibility` (Gemini/OpenAI → summaries; Bedrock/Meta → none; OpenAI-compatible vendors with documented reasoning fields → full deltas). Set it explicitly only when the model deviates from its family. -- After inserting the entry, run `bun run agent-stream-docs:generate` and commit the regenerated `apps/docs/content/docs/en/workflows/blocks/agent.mdx` — CI diffs it. +- After inserting the entry, run `bun run agent-stream-docs:generate` and commit the regenerated `apps/docs/content/docs/workflows/blocks/agent.mdx` — CI diffs it. - Include the `streamed` value (with its source URL) in the verification report when set. ### Wrong family entirely? diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa1be99ea97..c81d49b83f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -630,7 +630,7 @@ jobs: with: filters: | docs: - - 'apps/docs/content/docs/en/**' + - 'apps/docs/content/docs/**' - 'apps/sim/scripts/process-docs.ts' - 'apps/sim/lib/chunkers/**' diff --git a/.gitignore b/.gitignore index 6e9bdf2c99c..4371d6d73f7 100644 --- a/.gitignore +++ b/.gitignore @@ -86,7 +86,6 @@ start-collector.sh ## Helm Chart Tests helm/sim/test -i18n.cache ## Claude Code .claude/launch.json diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[[...slug]]/page.tsx similarity index 80% rename from apps/docs/app/[lang]/[[...slug]]/page.tsx rename to apps/docs/app/[[...slug]]/page.tsx index 87436d3f664..02f782880e6 100644 --- a/apps/docs/app/[lang]/[[...slug]]/page.tsx +++ b/apps/docs/app/[[...slug]]/page.tsx @@ -1,6 +1,5 @@ import type React from 'react' import { highlight } from 'fumadocs-core/highlight' -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' @@ -16,13 +15,11 @@ import { APIExampleSelector } from '@/components/ui/api-example-selector' 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, getAuthenticatedCodeSamples, openapi } from '@/lib/openapi' import { simShikiOptions } from '@/lib/shiki-theme' 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 /** @@ -40,37 +37,6 @@ function isContentHeading(item: { url: string }): boolean { return !ONWARD_NAV_SLUG.test(item.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 } -} - -/** - * Strips a leading `/{lang}` path segment from a page URL. Unlike a naive - * `String.replace`, this only removes the locale when it is actually the - * first path segment — a plain substring replace would also match `/en` - * inside unrelated slugs (e.g. `/platform/enterprise`, `/integrations/enrich`, - * `/platform/self-hosting/environment-variables`), corrupting canonical and - * hreflang URLs for those pages. - */ -function stripLocalePrefix(url: string, lang: string): string { - const prefix = `/${lang}` - if (url === prefix) return '' - if (url.startsWith(`${prefix}/`)) return url.slice(prefix.length) - return url -} - /** * Renders the API reference's request and response samples through the docs' own `CodeBlock` * rather than fumadocs-openapi's built-in one, so those blocks get the emcn copy control @@ -122,10 +88,9 @@ const APIPage = createAPIPage(openapi, { }, }) -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) +export default async function Page(props: { params: Promise<{ slug?: string[] }> }) { + const { slug } = await props.params + const page = source.getPage(slug) if (!page) notFound() const data = page.data as unknown as PageData & { @@ -139,9 +104,7 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l const isAcademy = slug?.[0] === 'academy' const isCli = slug?.[0] === 'cli' - const pageTreeRecord = source.pageTree as Record - const pageTree = pageTreeRecord[lang] ?? pageTreeRecord.en ?? Object.values(pageTreeRecord)[0] - const rawNeighbours = pageTree ? findNeighbour(pageTree, page.url) : null + const rawNeighbours = findNeighbour(source.pageTree, page.url) // Academy, API Reference, and CLI 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 (`/`). @@ -173,11 +136,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l let currentPath = '' urlParts.forEach((part: string, index: number) => { - if (index === 0 && SUPPORTED_LANGUAGES.has(part)) { - currentPath = `/${part}` - return - } - currentPath += `/${part}` const name = part @@ -218,7 +176,6 @@ export default async function Page(props: { params: Promise<{ slug?: string[]; l title={data.title} description={data.description || ''} url={`${BASE_URL}${page.url}`} - lang={lang} breadcrumb={breadcrumbs} /> -}) { - const params = await props.params - const { lang, slug } = resolveLangAndSlug(params) - const page = source.getPage(slug, lang) +export async function generateMetadata(props: { params: Promise<{ slug?: string[] }> }) { + const { slug } = await props.params + const page = source.getPage(slug) if (!page) notFound() const data = page.data as unknown as PageData @@ -377,13 +330,7 @@ export async function generateMetadata(props: { 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 - }, []), + locale: 'en_US', images: [ { url: ogImageUrl, @@ -406,15 +353,6 @@ export async function generateMetadata(props: { canonical: fullUrl, alternates: { canonical: fullUrl, - languages: { - 'x-default': `${BASE_URL}${stripLocalePrefix(page.url, lang)}`, - en: `${BASE_URL}${stripLocalePrefix(page.url, lang)}`, - es: `${BASE_URL}/es${stripLocalePrefix(page.url, lang)}`, - fr: `${BASE_URL}/fr${stripLocalePrefix(page.url, lang)}`, - de: `${BASE_URL}/de${stripLocalePrefix(page.url, lang)}`, - ja: `${BASE_URL}/ja${stripLocalePrefix(page.url, lang)}`, - zh: `${BASE_URL}/zh${stripLocalePrefix(page.url, lang)}`, - }, }, } } diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx deleted file mode 100644 index 90079d4025f..00000000000 --- a/apps/docs/app/[lang]/layout.tsx +++ /dev/null @@ -1,129 +0,0 @@ -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 { Inter } from 'next/font/google' -import { ThemeProvider } from 'next-themes' -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 { 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 = 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 ( - - -