Files
copilotkit__copilotkit/showcase/shell-docs/src/app/[framework]/[[...slug]]/page.tsx
T

1437 lines
50 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// /<framework>/<...slug> — framework-scoped docs route.
//
// Example URLs:
// /langgraph-python → framework landing page
// /langgraph-python/agentic-chat-ui → the Agentic Chat UI docs
// with snippets resolved from
// the `langgraph-python`
// integration's cells
// /mastra/generative-ui/tool-rendering → tool rendering docs scoped
// to the `mastra` cells
//
// The first URL segment is validated against the registry's list of
// integration slugs. When it doesn't match, we fall through to
// UnscopedDocsPage so unscoped doc slugs (e.g. /quickstart) are served
// correctly even though Next.js routes them here before [[...slug]].
import React from "react";
import type { Metadata } from "next";
import { notFound, redirect } from "next/navigation";
import Link from "next/link";
import { MDXRemote } from "next-mdx-remote/rsc";
import remarkGfm from "remark-gfm";
import {
rehypeCode,
rehypeCodeDefaultOptions,
} from "fumadocs-core/mdx-plugins";
import { ShellDocsLayout } from "@/components/shell-docs-layout";
import { DocsPage } from "fumadocs-ui/page";
import { navTreeToPageTree } from "@/lib/page-tree-bridge";
import { DocsPageView } from "@/components/docs-page-view";
import { MdxCodeBlock } from "@/components/mdx-code-block";
import { SidebarFrameworkSelector } from "@/components/sidebar-framework-selector";
import { UnscopedDocsPage } from "@/components/unscoped-docs-page";
import { FrameworkOverview } from "@/components/content/landing-pages/framework-overview";
import { MdxFrameworkOverview } from "@/components/content/landing-pages/mdx-framework-overview";
import type { MdxFrameworkOverviewProps } from "@/components/content/landing-pages/mdx-framework-overview";
import { FrameworkSetup } from "@/lib/setup-concept";
import { frameworkOverviews } from "@/data/frameworks";
import {
getAngularDocsNavTree,
resolveAngularDoc,
} from "@/lib/angular-doc-navigation";
import { buildAngularBackendOverview } from "@/lib/angular-backend-overview";
import { docsComponents } from "@/lib/mdx-registry";
import { resolveFrontendDocPage } from "@/lib/frontend-doc-policy";
import { resolveFrameworkContent } from "@/lib/framework-content-resolution";
import {
getFrontendGuidanceContentSlug,
getFrontendContentSlug,
getFrontendCanonicalSlug,
getFrontendQuickstartNavTree,
} from "@/lib/frontend-page-content";
import type { FrontendPageId } from "@/lib/frontend-page-content";
import {
frontendPathForBackend,
getFrontendOption,
isChannelFrontend,
isFrontendId,
parseFrontendRoutePath,
} from "@/lib/frontend-options";
import { resolveChannelGuideRoute } from "@/lib/channel-guide-routes";
import type { ChannelFrontend } from "@/lib/channel-guide-routes";
import { transformerMeta } from "@/lib/rehype-code-meta";
import { onboardingFrameworkFor } from "@/lib/docs-onboarding-framework";
import { onboardingFrontendFor } from "@/lib/docs-onboarding-frontend";
import {
CONTENT_DIR,
buildFrameworkNav,
buildFrameworkOnlyNav,
findFrameworksWithCell,
findFrameworksWithPage,
loadDoc,
} from "@/lib/docs-render";
import type { NavNode } from "@/lib/docs-render";
import {
getDocsFolder,
getDocsMode,
getIntegration,
getIntegrations,
ROOT_FRAMEWORK,
} from "@/lib/registry";
import { buildDocMetadata } from "@/lib/seo-metadata";
import { RESERVED_ROUTE_SLUGS } from "@/lib/reserved-route-slugs";
import demoContent from "@/data/demo-content.json";
import fs from "fs";
import path from "path";
const DOCS_ONLY_FRAMEWORK_SLUGS = new Set(["a2a", "agent-spec", "deepagents"]);
function hasDocsOnlyFrameworkContent(framework: string): boolean {
if (!DOCS_ONLY_FRAMEWORK_SLUGS.has(framework)) return false;
return (
frameworkOverviews[framework] !== undefined ||
fs.existsSync(path.join(CONTENT_DIR, "integrations", framework))
);
}
function isFrontendPageId(value: string): value is FrontendPageId {
return isFrontendId(value) && value !== "react";
}
function frontendRoutePath(
frontend: FrontendPageId,
slugPath: string,
activeBackendFramework: string | null = null,
): string {
return frontendPathForBackend(frontend, slugPath, activeBackendFramework);
}
function isFrontendGuidanceSlug(slugPath: string): boolean {
return slugPath === "using-these-docs";
}
function isFrontendRootSlug(slugPath: string): boolean {
return !slugPath || slugPath === "quickstart";
}
function reactRootPath(slugPath: string): string {
return frontendPathForBackend("react", slugPath);
}
function scopedRoutePath(slugHrefPrefix: string, slugPath: string): string {
const prefix = slugHrefPrefix.replace(/\/+$/, "");
const normalizedSlugPath = slugPath.split("/").filter(Boolean).join("/");
if (!normalizedSlugPath) return prefix || "/";
return `${prefix}/${normalizedSlugPath}`;
}
function legacyFrontendPathRedirect(
activeBackendFramework: string,
slugPath: string,
): string | null {
const [frontendsSegment, frontend, ...tail] = slugPath
.split("/")
.filter(Boolean);
if (frontendsSegment !== "frontends" || !isFrontendId(frontend)) {
return null;
}
return frontendPathForBackend(
frontend,
tail.join("/"),
activeBackendFramework,
);
}
function frontendMetadata(
frontend: FrontendPageId,
slugPath: string,
activeBackendFramework: string | null = null,
): Metadata {
if (isChannelFrontend(frontend) && !slugPath) {
const doc = loadDoc("channels");
const frontendName =
frontend === "teams"
? "Microsoft Teams"
: getFrontendOption(frontend).name;
return buildDocMetadata({
title: `${frontendName}: ${doc?.fm.title ?? "Channels"}`,
description: doc?.fm.description,
canonicalPath: frontendRoutePath(frontend, "", activeBackendFramework),
});
}
if (isChannelFrontend(frontend) && slugPath === "connect") {
const contentSlug = getFrontendContentSlug(frontend);
const doc = loadDoc(contentSlug);
return buildDocMetadata({
title: doc?.fm.title ?? "Connect and run your agent",
description: doc?.fm.description,
canonicalPath: frontendRoutePath(
frontend,
"connect",
activeBackendFramework,
),
});
}
if (!slugPath || slugPath === "quickstart") {
const contentSlug = getFrontendContentSlug(frontend);
const doc = loadDoc(contentSlug);
const option = getFrontendOption(frontend);
return buildDocMetadata({
title: `${doc?.fm.title ?? option.name} quickstart`,
description: doc?.fm.description,
canonicalPath: frontendRoutePath(frontend, "", activeBackendFramework),
});
}
if (slugPath === "using-these-docs") {
const doc = loadDoc(getFrontendGuidanceContentSlug(frontend));
const option = getFrontendOption(frontend);
return buildDocMetadata({
title: `${option.name}: ${doc?.fm.title ?? "using these docs"}`,
description: doc?.fm.description,
canonicalPath: frontendRoutePath(
frontend,
slugPath,
activeBackendFramework,
),
});
}
const resolution = resolveFrontendDocPage(frontend, slugPath);
const doc =
resolution.status === "found" ? loadDoc(resolution.contentSlugPath) : null;
return buildDocMetadata({
title: doc?.fm.title ?? slugPath,
description: doc?.fm.description,
canonicalPath:
resolution.status === "found"
? frontendRoutePath(
frontend,
resolution.slugPath,
activeBackendFramework,
)
: frontendRoutePath(frontend, slugPath, activeBackendFramework),
});
}
function frameworkMetadata(
framework: string,
slugPath: string,
canonicalPath = slugPath ? `/${framework}/${slugPath}` : `/${framework}`,
): Metadata {
// Try to read frontmatter for the resolved page. Mirror the page's
// own content-resolution order (authored vs generated, per-framework
// override vs root) cheaply: best-effort only; if nothing resolves,
// the helper falls back to the framework slug as a humanised title.
let title: string | undefined;
let description: string | undefined;
const integration = getIntegration(framework);
const isDocsOnlyFramework =
!integration && hasDocsOnlyFrameworkContent(framework);
if (!integration && !isDocsOnlyFramework) {
// Root-surface URL. The BIA-authored page wins when one exists —
// mirror UnscopedDocsPage's resolution so the metadata matches the
// content the route serves.
const unscopedPath = [
framework,
...slugPath.split("/").filter(Boolean),
].join("/");
const doc =
loadDoc(
`integrations/${getDocsFolder(ROOT_FRAMEWORK)}/${unscopedPath}`,
) ?? loadDoc(unscopedPath);
title = doc?.fm.title ?? humanizeSlug(unscopedPath);
description = doc?.fm.description;
} else if (slugPath) {
const docsFolder = getDocsFolder(framework);
const frameworkScopedDoc = loadDoc(
`integrations/${docsFolder}/${slugPath}`,
);
const doc = frameworkScopedDoc ?? loadDoc(slugPath);
if (doc) {
title = doc.fm.title;
description = doc.fm.description;
}
} else {
// Framework root — prefer the integration record's display name and
// tagline, falling back to the framework's index.mdx if present.
const overview = frameworkOverviews[framework];
const indexDoc = loadDoc(`integrations/${getDocsFolder(framework)}/index`);
title =
indexDoc?.fm.title ??
overview?.frameworkName ??
integration?.name ??
framework;
description = indexDoc?.fm.description ?? overview?.subheader;
}
// Per-page OG route lives at /og/<slug>/og.png — see
// src/app/og/[...slug]/route.tsx. Each framework variant gets its own
// image because the slug is framework-scoped.
const ogPath = `/og${canonicalPath}/og.png`;
return buildDocMetadata({
title: title ?? framework,
description,
canonicalPath,
ogPath,
});
}
// Per-framework self-canonical: /<framework>/<slug> declares itself
// canonical (NOT the bare /<slug>) so search engines index each
// framework variant at its own URL. When the URL's first segment
// doesn't match a registered integration, the route falls through to
// UnscopedDocsPage but the canonical still points at the same URL —
// the page's identity is defined by its URL, not the resolution
// strategy used to render it.
//
// Title and description come from the resolved MDX frontmatter (with
// the same per-framework override resolution the page render uses) so
// every variant emits its own social card and SEO description rather
// than inheriting the layout's generic site-wide values.
export async function generateMetadata({
params,
}: {
params: Promise<{ framework: string; slug?: string[] }>;
}): Promise<Metadata> {
const { framework, slug } = await params;
const slugPath = slug?.join("/") ?? "";
if (framework === "react") {
const canonicalPath = reactRootPath(slugPath);
const doc = slugPath
? (loadDoc(`integrations/${getDocsFolder(ROOT_FRAMEWORK)}/${slugPath}`) ??
loadDoc(slugPath))
: null;
return buildDocMetadata({
title: doc?.fm.title ?? (slugPath || "CopilotKit docs"),
description: doc?.fm.description,
canonicalPath,
ogPath: `/og${canonicalPath}/og.png`,
});
}
if (isFrontendPageId(framework)) {
const frontendRoute = parseFrontendRoutePath(
`/${[framework, ...(slug ?? [])].join("/")}`,
getIntegrations().map((integration) => integration.slug),
);
const activeBackendFramework =
frontendRoute?.backend === ROOT_FRAMEWORK
? null
: (frontendRoute?.backend ?? null);
const activeFrontendSlugPath = frontendRoute?.slugPath ?? slugPath;
if (
activeBackendFramework &&
getDocsMode(activeBackendFramework) === "hidden"
) {
notFound();
}
if (isFrontendGuidanceSlug(activeFrontendSlugPath)) {
return frontendMetadata(
framework,
activeFrontendSlugPath,
activeBackendFramework,
);
}
if (isChannelFrontend(framework) && activeFrontendSlugPath === "connect") {
return frontendMetadata(
framework,
activeFrontendSlugPath,
activeBackendFramework,
);
}
if (
isFrontendRootSlug(activeFrontendSlugPath) &&
!(
framework === "angular" &&
activeBackendFramework &&
!activeFrontendSlugPath
)
) {
return frontendMetadata(
framework,
activeFrontendSlugPath,
activeBackendFramework,
);
}
const channelGuideRoute = resolveChannelGuideRoute({
frontend: framework,
framework: activeBackendFramework,
slugPath: activeFrontendSlugPath,
frameworkDocsMode: getDocsMode(activeBackendFramework ?? ROOT_FRAMEWORK),
});
if (channelGuideRoute) {
const doc = loadDoc(channelGuideRoute.sourceSlug);
const frontendName =
channelGuideRoute.frontend === "teams"
? "Microsoft Teams"
: getFrontendOption(channelGuideRoute.frontend).name;
const backendName =
channelGuideRoute.framework !== ROOT_FRAMEWORK
? (getIntegration(channelGuideRoute.framework)?.name ??
humanizeSlug(channelGuideRoute.framework))
: null;
const metadataTitlePrefix = backendName
? `${frontendName} + ${backendName}`
: frontendName;
const canonicalPath = channelGuideRoute.canonicalPath;
return buildDocMetadata({
title: `${metadataTitlePrefix}: ${
doc?.fm.title ?? humanizeSlug(channelGuideRoute.slugPath)
}`,
description: doc?.fm.description,
canonicalPath,
ogPath: `/og${canonicalPath}/og.png`,
});
}
if (framework === "angular" && activeFrontendSlugPath) {
const canonicalSlugPath = getFrontendCanonicalSlug(
framework,
activeFrontendSlugPath,
);
const resolution = resolveAngularDoc(
activeBackendFramework,
canonicalSlugPath,
);
const doc = resolution ? loadDoc(resolution.contentSlugPath) : null;
const canonicalPath = frontendRoutePath(
framework,
canonicalSlugPath,
activeBackendFramework,
);
return buildDocMetadata({
title: doc?.fm.title ?? canonicalSlugPath,
description: doc?.fm.description,
canonicalPath,
ogPath: `/og${canonicalPath}/og.png`,
});
}
if (activeBackendFramework) {
const slugHrefPrefix = frontendRoutePath(
framework,
"",
activeBackendFramework,
);
return frameworkMetadata(
activeBackendFramework,
activeFrontendSlugPath,
scopedRoutePath(slugHrefPrefix, activeFrontendSlugPath),
);
}
return frontendMetadata(
framework,
activeFrontendSlugPath,
activeBackendFramework,
);
}
return frameworkMetadata(framework, slugPath);
}
export async function generateStaticParams() {
// Rely on the catch-all's dynamic behaviour at runtime; returning an
// empty array keeps build times short since there are ~17 frameworks
// × ~60 doc pages, all cheap to render on demand.
return [];
}
// Force dynamic rendering so paths NOT in generateStaticParams are
// rendered fresh on each request. Without this, Next.js was caching
// the rendered "404 page body" with a 200 status and `s-maxage=1y`
// (a soft-404 that demotes the whole site in search rankings). With
// `force-dynamic`, the runtime notFound() call sets the response
// status to 404 every time. The data fetches here are filesystem
// reads of MDX, so per-request rendering is cheap.
export const dynamic = "force-dynamic";
interface DemoRecord {
regions?: Record<string, unknown>;
}
const demos: Record<string, DemoRecord> = (
demoContent as { demos: Record<string, DemoRecord> }
).demos;
/**
* Heuristic: does this framework have ANY region tagged for the given
* feature slug? Used to render a clear "not available" banner when an
* MDX page's snippet_cell points at a cell that doesn't exist for the
* currently selected framework.
*/
function frameworkHasCellFor(framework: string, cell: string): boolean {
return Boolean(demos[`${framework}::${cell}`]);
}
export default async function FrameworkScopedDocsPage({
params,
}: {
params: Promise<{ framework: string; slug?: string[] }>;
}) {
const { framework, slug } = await params;
// Defense in depth: explicitly 404 on reserved top-level route slugs.
// Next.js already prefers exact-match routes over this catch-all, so
// `/docs`, `/ag-ui`, etc. never reach here during normal routing.
// But if the registry ever ships an integration whose slug collides
// with a reserved segment, layout.tsx drops it from knownFrameworks
// AND this guard ensures the route handler still short-circuits to a
// clean 404 rather than rendering garbage.
if ((RESERVED_ROUTE_SLUGS as readonly string[]).includes(framework)) {
notFound();
}
const frontendSlugPath = slug?.join("/") ?? "";
if (framework === "frontends") {
const [frontend, ...tail] = slug ?? [];
if (isFrontendId(frontend)) {
redirect(frontendPathForBackend(frontend, tail.join("/")));
}
redirect(frontendSlugPath ? `/${frontendSlugPath}` : "/");
}
if (framework === "react") {
redirect(reactRootPath(frontendSlugPath));
}
let scopedFramework = framework;
let scopedSlug = slug;
let scopedSlugHrefPrefix: string | null = null;
let activeFrontendPage: FrontendPageId | null = null;
if (isFrontendPageId(framework)) {
activeFrontendPage = framework;
const frontendRoute = parseFrontendRoutePath(
`/${[framework, ...(slug ?? [])].join("/")}`,
getIntegrations().map((integration) => integration.slug),
);
const activeBackendFramework = frontendRoute?.backend ?? null;
const requestedFrontendSlugPath =
frontendRoute?.slugPath ?? frontendSlugPath;
const activeFrontendSlugPath = getFrontendCanonicalSlug(
framework,
requestedFrontendSlugPath,
);
if (activeFrontendSlugPath !== requestedFrontendSlugPath) {
redirect(
frontendRoutePath(
framework,
activeFrontendSlugPath,
activeBackendFramework,
),
);
}
if (activeBackendFramework === ROOT_FRAMEWORK) {
redirect(frontendRoutePath(framework, activeFrontendSlugPath));
}
if (
activeBackendFramework &&
getDocsMode(activeBackendFramework) === "hidden"
) {
notFound();
}
if (!activeFrontendSlugPath) {
if (isChannelFrontend(framework)) {
return (
<ChannelGuideDocsPage
frontend={framework}
activeBackendFramework={activeBackendFramework}
slugPath=""
contentSlugPath="channels"
/>
);
}
if (framework === "angular" && activeBackendFramework) {
return (
<FrameworkRootPage
framework={activeBackendFramework}
preferIndexMdx
frontendOverride="angular"
slugHrefPrefix={frontendRoutePath(
framework,
"",
activeBackendFramework,
)}
navTreeOverride={getAngularDocsNavTree(activeBackendFramework)}
sidebarBannerSlot={<FrontendSidebarBanner frontend={framework} />}
/>
);
}
return (
<FrontendQuickstartDocsPage
frontend={framework}
activeBackendFramework={activeBackendFramework}
navTree={
framework === "angular"
? getAngularDocsNavTree(activeBackendFramework)
: undefined
}
/>
);
}
if (isChannelFrontend(framework) && activeFrontendSlugPath === "connect") {
return (
<FrontendQuickstartDocsPage
frontend={framework}
activeBackendFramework={activeBackendFramework}
routeSlugPath="connect"
/>
);
}
if (activeFrontendSlugPath === "quickstart") {
if (framework === "angular" && activeBackendFramework) {
return (
<FrontendQuickstartDocsPage
frontend={framework}
activeBackendFramework={activeBackendFramework}
routeSlugPath="quickstart"
navTree={getAngularDocsNavTree(activeBackendFramework)}
/>
);
}
redirect(frontendRoutePath(framework, "", activeBackendFramework));
}
if (isFrontendGuidanceSlug(activeFrontendSlugPath)) {
return (
<FrontendGuidanceDocsPage
frontend={framework}
activeBackendFramework={activeBackendFramework}
navTree={
framework === "angular"
? getAngularDocsNavTree(activeBackendFramework)
: undefined
}
/>
);
}
const channelGuideRoute = resolveChannelGuideRoute({
frontend: framework,
framework: activeBackendFramework,
slugPath: activeFrontendSlugPath,
frameworkDocsMode: getDocsMode(activeBackendFramework ?? ROOT_FRAMEWORK),
});
if (channelGuideRoute) {
return (
<ChannelGuideDocsPage
frontend={channelGuideRoute.frontend}
activeBackendFramework={
channelGuideRoute.framework === ROOT_FRAMEWORK
? null
: channelGuideRoute.framework
}
slugPath={channelGuideRoute.slugPath}
contentSlugPath={channelGuideRoute.sourceSlug}
/>
);
}
if (framework === "angular") {
const resolution = resolveAngularDoc(
activeBackendFramework,
activeFrontendSlugPath,
);
if (!resolution) notFound();
return (
<DocsPageView
slugPath={resolution.slugPath}
contentSlugPath={resolution.contentSlugPath}
slugHrefPrefix={frontendRoutePath(
framework,
"",
activeBackendFramework,
)}
frameworkOverride={resolution.framework}
onboardingFramework={onboardingFrameworkFor(resolution.framework)}
// `framework` is the URL's first segment and this branch has already
// narrowed it to `angular`, so the URL is what names the frontend.
onboardingFrontend={onboardingFrontendFor(`/${framework}`)}
frontendOverride="angular"
navTree={getAngularDocsNavTree(activeBackendFramework)}
sidebarBannerSlot={<FrontendSidebarBanner frontend={framework} />}
/>
);
}
if (activeBackendFramework) {
scopedFramework = activeBackendFramework;
scopedSlug = activeFrontendSlugPath
? activeFrontendSlugPath.split("/").filter(Boolean)
: undefined;
scopedSlugHrefPrefix = frontendRoutePath(
framework,
"",
activeBackendFramework,
);
} else {
const resolution = resolveFrontendDocPage(
framework,
activeFrontendSlugPath,
);
if (resolution.status === "not-found") notFound();
return (
<DocsPageView
slugPath={resolution.slugPath}
contentSlugPath={resolution.contentSlugPath}
slugHrefPrefix={frontendRoutePath(
framework,
"",
activeBackendFramework,
)}
frameworkOverride={activeBackendFramework}
// This is the `else` of `if (activeBackendFramework)`, so the URL
// carries no backend segment — which on a frontend route is exactly
// how the Built-in Agent is spelled: `/vue/built-in-agent/<slug>`
// redirects here, and the framework selector shows the Built-in
// Agent as the active backend. So `/vue/<slug>` and
// `/vue/mastra/<slug>` are the same page under two framework
// selections and both name theirs in the copied prompt.
//
// `frameworkOverride` stays null: no backend segment still means no
// framework-scoped snippet resolution for this content.
onboardingFramework={onboardingFrameworkFor(
activeBackendFramework ?? ROOT_FRAMEWORK,
)}
// Inside `isFrontendPageId(framework)`, so the URL's first segment
// is the frontend the reader selected.
onboardingFrontend={onboardingFrontendFor(`/${framework}`)}
frontendOverride={framework}
navTree={getFrontendQuickstartNavTree(framework)}
sidebarBannerSlot={<FrontendSidebarBanner frontend={framework} />}
/>
);
}
}
// Validate the framework slug against the registry.
// If not a registered integration, treat the URL as an unscoped doc path.
// This is necessary because Next.js routes /quickstart here (dynamic segment
// beats optional catch-all) before [[...slug]] ever sees it.
//
// Exception: docs-only frameworks (`a2a`, `agent-spec`, `deepagents`) have a
// `frameworkOverviews` entry and/or content under `integrations/<slug>/`
// but no demo package in `showcase/integrations/`, so they're absent from
// the registry. Recognize them by slug so the framework-root page (Tier 1
// FrameworkOverview / Tier 2 MDX index) can still render.
const integration = getIntegration(scopedFramework);
const isDocsOnlyFramework =
!integration && hasDocsOnlyFrameworkContent(scopedFramework);
if (!integration && !isDocsOnlyFramework) {
const unscopedPath = [scopedFramework, ...(scopedSlug ?? [])].join("/");
return <UnscopedDocsPage slugPath={unscopedPath} />;
}
// `docs_mode: hidden` (manifest.yaml) means the framework should not
// appear in shell-docs at all — no `/<slug>` page, no switcher entry.
// 404 is the right answer; the unscoped fallback above would still
// show the user the agnostic docs under their framework slug, which
// misleadingly implies the framework has docs.
if (integration && getDocsMode(scopedFramework) === "hidden") {
notFound();
}
const slugPath = scopedSlug?.join("/") ?? "";
const frontendRedirect = legacyFrontendPathRedirect(
scopedFramework,
slugPath,
);
if (frontendRedirect) redirect(frontendRedirect);
// No slug → framework landing page. Three-tier resolution:
// 1. Data-driven `FrameworkOverview` when a record exists in
// `frameworkOverviews` (13 frameworks).
// 2. MDX-authored `integrations/<folder>/index.mdx` when present
// (built-in-agent + deepagents are fully free-form).
// 3. Fallback: 404. Every registered integration is expected to
// have either a data record OR an index.mdx after Phase 2; a
// missing entry is an authoring error worth surfacing.
if (!slugPath) {
return (
<FrameworkRootPage
framework={scopedFramework}
preferIndexMdx={Boolean(scopedSlugHrefPrefix)}
slugHrefPrefix={scopedSlugHrefPrefix ?? `/${scopedFramework}`}
/>
);
}
// `/<framework>/unselected/<path>` is incoherent — a framework IS
// selected, so the URL should never assert the "unselected" state
// alongside. Collapse to the framework-scoped path (which serves the
// same underlying content, just with Snippets resolved against the
// selected framework's cells).
if (slugPath.startsWith("unselected/")) {
redirect(
`${scopedSlugHrefPrefix ?? `/${scopedFramework}`}/${slugPath.slice(
"unselected/".length,
)}`,
);
}
// Content resolution:
// 1. Root MDX — framework-agnostic page rendered with this
// framework's override (Model 1, the primary path).
// 2. Per-framework override at `integrations/<framework>/<slug>.mdx`
// — topics that are genuinely framework-specific (e.g. BIA's
// `server-tools`) and have no root equivalent. When this path
// wins, we record it as `contentSlugPath` so DocsPageView loads
// from there while the URL slug continues driving breadcrumbs
// and active-link detection.
// 3. If the slug exists for *other* frameworks but not this one,
// render a "not available for <framework>" fallback inside the
// docs shell (handled below, after the nav is built).
// 4. Otherwise 404.
// Most registry slugs map 1:1 to a folder under `integrations/`, but
// language/runtime variants share a single docs folder:
// langgraph-python/typescript/fastapi → `langgraph/`, ms-agent-dotnet/
// python → `microsoft-agent-framework/`, plus legacy renames for
// google-adk → `adk/` and strands → `aws-strands/`. Resolve the URL
// slug to its docs folder before touching disk.
const docsFolder = getDocsFolder(scopedFramework);
const docsMode = getDocsMode(scopedFramework);
const frameworkName =
integration?.name ??
frameworkOverviews[scopedFramework]?.frameworkName ??
scopedFramework;
const resolvedContent = resolveFrameworkContent(scopedFramework, slugPath);
const contentSlugPath = resolvedContent?.contentSlugPath ?? slugPath;
const doc = resolvedContent?.doc ?? null;
// Authored integrations own their full docs tree and sidebar IA.
// Generated integrations use the root docs IA with a sparse
// framework-specific override section.
const navTree: NavNode[] =
docsMode === "authored"
? buildFrameworkOnlyNav(docsFolder)
: buildFrameworkNav(docsFolder, frameworkName, scopedFramework);
if (!doc) {
// No root MDX and no override for this framework. If the topic
// exists for *other* frameworks (e.g. a BIA-specific page like
// `/mastra/advanced-configuration`), render a fallback inside the
// docs shell that lists the frameworks where it does exist — the
// user keeps their framework context and gets a clear path
// forward. Only 404 when the slug is unknown everywhere.
const allFrameworkSlugs = getIntegrations().map((i) => i.slug);
const availableIn = findFrameworksWithPage(
slugPath,
allFrameworkSlugs,
getDocsFolder,
);
if (availableIn.length > 0) {
return (
<NotAvailableForFrameworkPage
slugPath={slugPath}
availableIn={availableIn}
navTree={navTree}
frameworkName={frameworkName}
frameworkSlug={scopedFramework}
slugHrefPrefix={scopedSlugHrefPrefix ?? `/${scopedFramework}`}
activeFrontendPage={activeFrontendPage}
/>
);
}
notFound();
}
// Detect whether this page's default cell (the feature) has any
// snippets tagged for the current framework. When it doesn't, show
// a prominent banner pointing the user at a framework that does.
const missingCell =
integration &&
doc.fm.defaultCell &&
!frameworkHasCellFor(scopedFramework, doc.fm.defaultCell);
const alternativeFrameworks = doc.fm.defaultCell
? findFrameworksWithCell(
doc.fm.defaultCell,
getIntegrations().map((i) => i.slug),
demos,
)
: [];
const banner = missingCell ? (
<div className="shell-docs-radius-surface shell-docs-warning-surface mb-6 border p-4 shadow-[var(--shadow-control)]">
<div className="text-sm font-semibold text-[var(--text)] mb-1">
Not available for {frameworkName} yet
</div>
<p className="text-[13px] text-[var(--text-secondary)] leading-relaxed">
This feature (<code>{doc.fm.defaultCell}</code>) hasn't been tagged in
any {frameworkName} cell yet.
{alternativeFrameworks.length > 0 && (
<>
{" "}
Try{" "}
{alternativeFrameworks.slice(0, 3).map((altSlug, i) => {
const alt = getIntegration(altSlug);
if (!alt) return null;
const name = alt.name;
const href = activeFrontendPage
? frontendRoutePath(activeFrontendPage, slugPath, altSlug)
: `/${altSlug}/${slugPath}`;
return (
<React.Fragment key={altSlug}>
{i > 0 && ", "}
<Link
href={href}
className="text-[var(--accent)] hover:underline"
>
{name}
</Link>
</React.Fragment>
);
})}
.
</>
)}
</p>
</div>
) : null;
return (
<DocsPageView
slugPath={slugPath}
contentSlugPath={contentSlugPath}
slugHrefPrefix={scopedSlugHrefPrefix ?? `/${scopedFramework}`}
frameworkOverride={scopedFramework}
onboardingFramework={onboardingFrameworkFor(scopedFramework)}
// The page's own URL prefix: `/vue/mastra` on a frontend route,
// `/mastra` off one. Only its first segment can name a frontend, so a
// backend-only prefix resolves to the default React frontend.
onboardingFrontend={onboardingFrontendFor(
scopedSlugHrefPrefix ?? `/${scopedFramework}`,
)}
frontendOverride={activeFrontendPage ?? undefined}
navTree={
activeFrontendPage
? getFrontendQuickstartNavTree(activeFrontendPage)
: navTree
}
bannerSlot={banner}
sidebarBannerSlot={
activeFrontendPage ? (
<FrontendSidebarBanner frontend={activeFrontendPage} />
) : undefined
}
/>
);
}
function ChannelGuideDocsPage({
frontend,
activeBackendFramework,
slugPath,
contentSlugPath,
}: {
frontend: ChannelFrontend;
activeBackendFramework: string | null;
slugPath: string;
contentSlugPath: string;
}) {
if (!loadDoc(contentSlugPath)) notFound();
return (
<DocsPageView
slugPath={slugPath}
contentSlugPath={contentSlugPath}
slugHrefPrefix={frontendRoutePath(frontend, "", activeBackendFramework)}
frameworkOverride={activeBackendFramework ?? ROOT_FRAMEWORK}
// A channel guide with no backend selected still documents the
// Built-in Agent, so `ROOT_FRAMEWORK` is the framework being read
// about here, not a placeholder.
onboardingFramework={onboardingFrameworkFor(
activeBackendFramework ?? ROOT_FRAMEWORK,
)}
// `frontend` is the URL's first segment on every route that reaches
// these components, so this is the frontend the URL asserts.
onboardingFrontend={onboardingFrontendFor(`/${frontend}`)}
frontendOverride={frontend}
navTree={getFrontendQuickstartNavTree(frontend)}
sidebarBannerSlot={<FrontendSidebarBanner frontend={frontend} />}
/>
);
}
function FrontendQuickstartDocsPage({
frontend,
activeBackendFramework,
routeSlugPath = "",
navTree,
}: {
frontend: FrontendPageId;
activeBackendFramework?: string | null;
routeSlugPath?: string;
navTree?: NavNode[];
}) {
const contentSlug = getFrontendContentSlug(frontend);
if (!loadDoc(contentSlug)) notFound();
return (
<DocsPageView
slugPath={routeSlugPath}
contentSlugPath={contentSlug}
slugHrefPrefix={frontendRoutePath(frontend, "", activeBackendFramework)}
frameworkOverride={
activeBackendFramework ??
(frontend === "slack" || frontend === "teams" ? ROOT_FRAMEWORK : null)
}
// No backend segment in the URL means the Built-in Agent is selected,
// for every frontend — unlike `frameworkOverride` above, which stays
// null off the channel frontends because their quickstart content is
// not framework-scoped.
onboardingFramework={onboardingFrameworkFor(
activeBackendFramework ?? ROOT_FRAMEWORK,
)}
// `frontend` is the URL's first segment on every route that reaches
// these components, so this is the frontend the URL asserts.
onboardingFrontend={onboardingFrontendFor(`/${frontend}`)}
frontendOverride={frontend}
navTree={navTree ?? getFrontendQuickstartNavTree(frontend)}
sidebarBannerSlot={<FrontendSidebarBanner frontend={frontend} />}
/>
);
}
function FrontendGuidanceDocsPage({
frontend,
activeBackendFramework,
navTree,
}: {
frontend: FrontendPageId;
activeBackendFramework?: string | null;
navTree?: NavNode[];
}) {
const contentSlug = getFrontendGuidanceContentSlug(frontend);
if (!loadDoc(contentSlug)) notFound();
return (
<DocsPageView
slugPath="using-these-docs"
contentSlugPath={contentSlug}
slugHrefPrefix={frontendRoutePath(frontend, "", activeBackendFramework)}
frameworkOverride={activeBackendFramework}
// Same rule as every other frontend route: no backend segment is the
// Built-in Agent, so `/vue/using-these-docs` names it where
// `/vue/mastra/using-these-docs` names Mastra.
onboardingFramework={onboardingFrameworkFor(
activeBackendFramework ?? ROOT_FRAMEWORK,
)}
// `frontend` is the URL's first segment on every route that reaches
// these components, so this is the frontend the URL asserts.
onboardingFrontend={onboardingFrontendFor(`/${frontend}`)}
frontendOverride={frontend}
navTree={navTree ?? getFrontendQuickstartNavTree(frontend)}
sidebarBannerSlot={<FrontendSidebarBanner frontend={frontend} />}
/>
);
}
function FrontendSidebarBanner(_props: { frontend: FrontendPageId }) {
return <SidebarFrameworkSelector />;
}
// ---------------------------------------------------------------------------
// Framework root page: renders the docs shell at the bare `/<framework>`
// URL using one of three content sources, tried in order:
//
// Tier 1. Data-driven `FrameworkOverview` from `frameworkOverviews`
// (13 frameworks). Optionally augmented with an after-features
// MDX escape hatch loaded from
// `src/content/framework-overviews/<slug>/after-features.mdx`.
// Tier 2. Free-form `integrations/<folder>/index.mdx`, rendered
// through the standard MDX pipeline. Used by built-in-agent
// and deepagents, which don't fit the FrameworkOverview shape.
// Tier 3. 404 — every registered integration should resolve via Tier
// 1 or Tier 2. A missing record + missing MDX is an authoring
// error.
//
// The sidebar / framework-selector chrome is identical to the per-doc
// `DocsPageView` rendering so the framework-root URL reads as part of
// the docs surface rather than a separate landing.
// ---------------------------------------------------------------------------
const FRAMEWORK_OVERVIEW_MDX_DIR = path.join(
process.cwd(),
"src/content/framework-overviews",
);
async function FrameworkRootPage({
framework,
preferIndexMdx = false,
slugHrefPrefix = `/${framework}`,
frontendOverride,
navTreeOverride,
sidebarBannerSlot,
}: {
framework: string;
preferIndexMdx?: boolean;
slugHrefPrefix?: string;
frontendOverride?: FrontendPageId;
navTreeOverride?: NavNode[];
sidebarBannerSlot?: React.ReactNode;
}) {
// Some frameworks are docs-only — they have a `frameworkOverviews`
// entry and an `integrations/<slug>/` content folder, but no demo
// package in `showcase/integrations/`, so `getIntegration()` returns
// undefined. Don't bail here — fall back to slug-derived inputs and
// let the Tier 1/2/3 cascade below decide whether to render or 404.
const integration = getIntegration(framework);
// Resolve the URL slug to its docs folder — see comment in
// FrameworkScopedDocsPage above. Authored frameworks get their own
// sidebar tree; generated frameworks get the merged root/override IA.
// `getDocsFolder` already falls back to the slug itself when there's
// no override, so it's safe for docs-only frameworks.
const docsFolder = getDocsFolder(framework);
// Display name preference: integration record → overview data →
// raw slug. Used as the framework-specific sidebar section header.
const integrationName =
integration?.name ??
frameworkOverviews[framework]?.frameworkName ??
framework;
const docsMode = getDocsMode(framework);
const navTree: NavNode[] =
navTreeOverride ??
(docsMode === "authored"
? buildFrameworkOnlyNav(docsFolder)
: buildFrameworkNav(docsFolder, integrationName, framework));
const indexContentPath = `integrations/${docsFolder}/index`;
const indexDoc = loadDoc(indexContentPath);
if (preferIndexMdx && docsMode !== "generated" && indexDoc) {
return (
<DocsPageView
slugPath=""
contentSlugPath={indexContentPath}
slugHrefPrefix={slugHrefPrefix}
frameworkOverride={framework}
onboardingFramework={onboardingFrameworkFor(framework)}
// `slugHrefPrefix` is this page's own URL prefix — `/angular/mastra`
// when a frontend route delegated here, `/<framework>` otherwise.
onboardingFrontend={onboardingFrontendFor(slugHrefPrefix)}
frontendOverride={frontendOverride}
navTree={navTree}
sidebarBannerSlot={sidebarBannerSlot}
/>
);
}
// Tier 1: data-driven FrameworkOverview. ONLY for `generated` mode —
// `authored` frameworks skip straight to Tier 2 so their ported
// index.mdx (not the auto-generated catalog landing) renders at
// `/<framework>`.
const overview = frameworkOverviews[framework];
if (overview && docsMode === "generated") {
let afterFeatures: React.ReactNode = undefined;
if (overview.hasAfterFeaturesMdx) {
const mdxPath = path.join(
FRAMEWORK_OVERVIEW_MDX_DIR,
framework,
"after-features.mdx",
);
if (fs.existsSync(mdxPath)) {
try {
const raw = fs.readFileSync(mdxPath, "utf-8");
afterFeatures = await MDXRemote({
source: raw,
components: {
...docsComponents,
// Mirror DocsPageView: wrap MDX-rendered <pre> blocks
// with figure chrome (copy button + optional file-path
// caption) so fenced code in after-features.mdx has the
// same affordances as fenced code on a regular docs
// page. `rehypeCodeMeta` (below) supplies the
// `data-title` / `data-language` data-attrs MdxCodeBlock
// reads.
pre: MdxCodeBlock,
// Bind the URL framework slug so any MdxFrameworkOverview
// usage inside after-features.mdx routes through the
// rewriter with the URL-active variant — same rationale
// as DocsPageView's components-map override.
FrameworkOverview: (props: MdxFrameworkOverviewProps) => (
<MdxFrameworkOverview
{...props}
currentFramework={framework ?? props.currentFramework}
hrefPrefix={slugHrefPrefix}
/>
),
// Mirror the binding in DocsPageView so any
// <FrameworkSetup> embedded in after-features.mdx also
// gets the URL framework slug threaded in.
FrameworkSetup: (props: {
concept: string;
heading?: string | null;
headingId?: string;
currentFramework?: string;
}) => (
<FrameworkSetup
{...props}
currentFramework={framework ?? props.currentFramework}
/>
),
},
options: {
mdxOptions: {
remarkPlugins: [remarkGfm],
// Fumadocs's Shiki-based `rehypeCode`; our
// `transformerMeta` Shiki transformer surfaces fence
// `title="..."` and the resolved language as data-attrs
// on the <pre> so MdxCodeBlock can render Fumadocs's
// CodeBlock figcaption + copy button.
rehypePlugins: [
[
rehypeCode,
{
fallbackLanguage: "plaintext",
transformers: [
...(rehypeCodeDefaultOptions.transformers ?? []),
transformerMeta(),
],
},
],
],
},
},
});
} catch (err) {
// Logged + swallowed: FrameworkOverview falls back to the
// structured `data.cta` block when `afterFeatures` is empty,
// so a transient read failure doesn't blank the page.
console.error(
`[framework-root] failed to read after-features.mdx for ${framework}`,
err,
);
}
} else {
console.error(
`[framework-root] hasAfterFeaturesMdx=true but file is missing: ${mdxPath}`,
);
}
}
const scopedOverview =
frontendOverride === "angular"
? buildAngularBackendOverview(overview, framework)
: overview;
return (
<FrameworkRootShell
navTree={navTree}
slugHrefPrefix={slugHrefPrefix}
sidebarBannerSlot={sidebarBannerSlot}
>
<FrameworkOverview
data={scopedOverview}
currentFramework={framework}
hrefPrefix={slugHrefPrefix}
frontendOverride={frontendOverride}
afterFeatures={afterFeatures}
/>
</FrameworkRootShell>
);
}
// Tier 2: free-form `integrations/<folder>/index.mdx`. Delegate to
// `DocsPageView` so the MDX renders through the same component map
// (Callout, Cards, OpsPlatformCTA, …) used by every other docs page.
// `slugPath=""` keeps active-link logic pointing at the framework
// root (the new `"index"`→`""` rewrite in buildFrameworkOverridesNav
// matches this).
if (indexDoc) {
return (
<DocsPageView
slugPath=""
contentSlugPath={indexContentPath}
slugHrefPrefix={slugHrefPrefix}
frameworkOverride={framework}
onboardingFramework={onboardingFrameworkFor(framework)}
// `slugHrefPrefix` is this page's own URL prefix — `/angular/mastra`
// when a frontend route delegated here, `/<framework>` otherwise.
onboardingFrontend={onboardingFrontendFor(slugHrefPrefix)}
frontendOverride={frontendOverride}
navTree={navTree}
sidebarBannerSlot={sidebarBannerSlot}
/>
);
}
// Tier 3: no data record AND no MDX index. Authoring gap.
notFound();
}
/**
* Sidebar + content-wrapper chrome shared with `DocsPageView`. Used by
* Tier 1 (data-driven FrameworkOverview) only; Tier 2 delegates to
* `DocsPageView` directly.
*/
function FrameworkRootShell({
navTree,
slugHrefPrefix,
sidebarBannerSlot,
children,
}: {
navTree: NavNode[];
slugHrefPrefix: string;
sidebarBannerSlot?: React.ReactNode;
children: React.ReactNode;
}) {
const pageTree = navTreeToPageTree(navTree, slugHrefPrefix);
return (
<ShellDocsLayout
tree={pageTree}
banner={sidebarBannerSlot ?? <SidebarFrameworkSelector />}
>
<DocsPage
toc={[]}
tableOfContent={{ enabled: false }}
tableOfContentPopover={{ enabled: false }}
breadcrumb={{ enabled: false }}
footer={{ enabled: false }}
>
<div className="docs-inner-content max-w-[900px] mx-auto px-4 md:px-6 pt-0 pb-6">
{children}
</div>
</DocsPage>
</ShellDocsLayout>
);
}
// ---------------------------------------------------------------------------
// "Not available for this framework" fallback. Rendered when the URL's
// slug has no root MDX, no override for the URL's framework, but DOES
// exist for one or more other frameworks (typically a BIA-specific
// page being hit under a different integration's scope). The goal is
// to keep the user in context — sidebar intact, framework switcher
// reachable — while pointing them at the frameworks where the page
// actually exists.
// ---------------------------------------------------------------------------
function NotAvailableForFrameworkPage({
slugPath,
availableIn,
navTree,
frameworkName,
frameworkSlug,
slugHrefPrefix = `/${frameworkSlug}`,
activeFrontendPage = null,
}: {
slugPath: string;
availableIn: string[];
navTree: NavNode[];
frameworkName: string;
frameworkSlug: string;
slugHrefPrefix?: string;
activeFrontendPage?: FrontendPageId | null;
}) {
const title = humanizeSlug(slugPath);
const sidebarNavTree = activeFrontendPage
? getFrontendQuickstartNavTree(activeFrontendPage)
: navTree;
const pageTree = navTreeToPageTree(sidebarNavTree, slugHrefPrefix);
return (
<ShellDocsLayout
tree={pageTree}
banner={
activeFrontendPage ? (
<FrontendSidebarBanner frontend={activeFrontendPage} />
) : (
<SidebarFrameworkSelector />
)
}
>
<DocsPage
toc={[]}
tableOfContent={{ enabled: false }}
tableOfContentPopover={{ enabled: false }}
breadcrumb={{ enabled: false }}
footer={{ enabled: false }}
>
<div className="docs-inner-content max-w-[900px] mx-auto px-4 md:px-6 pt-2 pb-6 md:pt-3 xl:pt-4">
<h1 className="text-[2rem] font-bold text-[var(--text)] tracking-tight mb-2 leading-tight">
{title}
</h1>
<p className="text-base text-[var(--text-muted)] mb-6 leading-relaxed">
This topic isn't available for {frameworkName}.
</p>
<div className="shell-docs-radius-surface shell-docs-warning-surface mb-6 border p-5 shadow-[var(--shadow-control)]">
<div className="text-sm font-semibold text-[var(--text)] mb-2">
Available in other integrations
</div>
<p className="text-[13px] text-[var(--text-secondary)] leading-relaxed mb-3">
<code>{slugPath}</code> is a topic specific to other integrations.
Pick one to continue reading:
</p>
<ul className="space-y-2">
{availableIn.map((slug) => {
const alt = getIntegration(slug);
if (!alt) return null;
const href = activeFrontendPage
? frontendRoutePath(activeFrontendPage, slugPath, slug)
: `/${slug}/${slugPath}`;
return (
<li key={slug}>
<Link
href={href}
className="text-sm text-[var(--accent)] hover:underline"
>
{alt.name}
</Link>
</li>
);
})}
</ul>
</div>
<p className="text-[13px] text-[var(--text-muted)]">
Or return to{" "}
<Link
href={slugHrefPrefix}
className="text-[var(--accent)] hover:underline"
>
the {frameworkName} docs
</Link>
.
</p>
</div>
</DocsPage>
</ShellDocsLayout>
);
}
function humanizeSlug(slugPath: string): string {
const last = slugPath.split("/").pop() ?? slugPath;
return last
.split("-")
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join(" ");
}