From 15ad8aa76a0db11d2236a57cb15c14ba396eecdb Mon Sep 17 00:00:00 2001 From: zahlekhan Date: Tue, 13 Oct 2026 16:16:29 +0530 Subject: [PATCH 1/2] docs: article structured data, search titles for comparison pages, fuller sitemap - Blog posts emit BlogPosting + BreadcrumbList JSON-LD and article:published_time, modified_time and author Open Graph tags. - Docs pages emit TechArticle JSON-LD with the git last-modified date. - Docs frontmatter accepts an optional `seoTitle` for , like blog posts; the H1 and sidebar keep `title`. Used on the A2UI package page and the OpenUI vs A2UI / json-render comparison page so their titles match searches. - /components is titled "Generative UI component library" (OG card keeps "OpenUI Design System"). - Sitemap adds /pricing, /cloud/gateway, /cloud/gateway/models, /cloud/observability, /openclaw-os, /demo/github and /components. Structured data ported from #615. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- docs/app/blog/[slug]/page.tsx | 43 +++++++++++++++++++- docs/app/components/metadata.ts | 6 ++- docs/app/sitemap.ts | 7 ++++ docs/components/docs-page.tsx | 31 +++++++++++++- docs/components/seo/JsonLd.tsx | 12 ++++++ docs/content/docs/api-reference/a2ui.mdx | 1 + docs/content/docs/openui-lang/comparison.mdx | 1 + docs/source.config.ts | 2 + 8 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 docs/components/seo/JsonLd.tsx diff --git a/docs/app/blog/[slug]/page.tsx b/docs/app/blog/[slug]/page.tsx index a0e5808bfe..05e13c7f90 100644 --- a/docs/app/blog/[slug]/page.tsx +++ b/docs/app/blog/[slug]/page.tsx @@ -1,8 +1,10 @@ import { GitHubButton } from "@/app/(home)/components/GitHubButton/GitHubButton"; import { Footer } from "@/app/(home)/sections/Footer/Footer"; +import { JsonLd } from "@/components/seo/JsonLd"; import { blogOgImage } from "@/lib/og/content"; +import { ogImageUrl } from "@/lib/og/images"; import { withOgImage } from "@/lib/page-metadata"; -import { blog } from "@/lib/source"; +import { BASE_URL, blog } from "@/lib/source"; import { getMDXComponents } from "@/mdx-components"; import { TOCProvider, TOCScrollArea } from "fumadocs-ui/components/toc"; import { TOCItem, TOCItems } from "fumadocs-ui/components/toc/default"; @@ -10,6 +12,44 @@ import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { BlogFaq } from "../components/BlogFaq"; +type BlogSourcePage = NonNullable<ReturnType<typeof blog.getPage>>; + +/* BlogPosting and breadcrumb structured data: gives Google the post's dates and author. */ +function blogStructuredData(page: BlogSourcePage) { + const url = `${BASE_URL}${page.url}`; + const { title, description, author, date } = page.data; + return [ + { + "@context": "https://schema.org", + "@type": "BlogPosting", + "@id": `${url}#article`, + headline: title, + description, + image: `${BASE_URL}${ogImageUrl(blogOgImage(page))}`, + datePublished: new Date(date).toISOString(), + dateModified: new Date(date).toISOString(), + // Bylines are a person or a team ("Thesys Engineering Team"). + author: { "@type": /\bteam\b/i.test(author) ? "Organization" : "Person", name: author }, + publisher: { + "@type": "Organization", + "@id": "https://www.thesys.dev/#organization", + name: "Thesys", + url: "https://www.thesys.dev", + }, + mainEntityOfPage: url, + url, + }, + { + "@context": "https://schema.org", + "@type": "BreadcrumbList", + itemListElement: [ + { "@type": "ListItem", position: 1, name: "Blog", item: `${BASE_URL}/blog` }, + { "@type": "ListItem", position: 2, name: title, item: url }, + ], + }, + ]; +} + export default async function BlogPostPage(props: { params: Promise<{ slug: string }> }) { const params = await props.params; const page = blog.getPage([params.slug]); @@ -23,6 +63,7 @@ export default async function BlogPostPage(props: { params: Promise<{ slug: stri return ( <TOCProvider toc={toc}> + <JsonLd data={blogStructuredData(page)} /> {/* Blend from the menu bar's surface color into a gray band, then back into the footer's surface — matching the home content band (light: foreground->background->foreground; dark: black -> neutral-950 -> black, so the edges meet the black menu + footer). */} diff --git a/docs/app/components/metadata.ts b/docs/app/components/metadata.ts index d3065d1ea1..e633f36e29 100644 --- a/docs/app/components/metadata.ts +++ b/docs/app/components/metadata.ts @@ -2,6 +2,8 @@ import type { PageMetadataInput } from "@/lib/page-metadata"; export default { pathname: "/components", - title: "OpenUI Design System", - description: "Foundation, Blocks, and Compose documentation", + title: "Generative UI component library", + description: + "The OpenUI Design System: charts, tables, form inputs and layout blocks that agents compose into Generative UI, with design foundations and a theme builder.", + openGraph: { title: "OpenUI Design System" }, } satisfies PageMetadataInput; diff --git a/docs/app/sitemap.ts b/docs/app/sitemap.ts index 196c287d31..6dd23406f1 100644 --- a/docs/app/sitemap.ts +++ b/docs/app/sitemap.ts @@ -10,6 +10,13 @@ const STATIC_PATHS = [ "/chat", "/demo/f1-dashboard", "/demo/trip-planner", + "/demo/github", + "/pricing", + "/cloud/gateway", + "/cloud/gateway/models", + "/cloud/observability", + "/openclaw-os", + "/components", "/blog", "/benchmarks", "/benchmarks/language", diff --git a/docs/components/docs-page.tsx b/docs/components/docs-page.tsx index 28e6747369..2b648b905a 100644 --- a/docs/components/docs-page.tsx +++ b/docs/components/docs-page.tsx @@ -1,8 +1,10 @@ import { LLMCopyButton, ViewOptions } from "@/components/ai/page-actions"; +import { JsonLd } from "@/components/seo/JsonLd"; import { gitConfig } from "@/lib/layout.shared"; import { docsOgImage } from "@/lib/og/content"; +import { ogImageUrl } from "@/lib/og/images"; import { withOgImage } from "@/lib/page-metadata"; -import { source } from "@/lib/source"; +import { BASE_URL, source } from "@/lib/source"; import { getMDXComponents, MdxLink } from "@/mdx-components"; import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/layouts/docs/page"; import { createRelativeLink } from "fumadocs-ui/mdx"; @@ -24,6 +26,7 @@ export function DocsPageView({ page }: { page: DocsSourcePage }) { // Without a sidebar there is room for the `full` page width next to the TOC (see global.css). data-no-sidebar={!page.data.sidebar || undefined} > + <JsonLd data={docsStructuredData(page)} /> {!page.data.customHeader && ( <> <DocsTitle>{page.data.title}</DocsTitle> @@ -57,9 +60,33 @@ export function getDocsPageMetadata(page: DocsSourcePage): Metadata { return withOgImage({ ...docsMetadata(page), description }, image, { description }); } +/* TechArticle structured data: gives Google the page's last-modified date and publisher. */ +function docsStructuredData(page: DocsSourcePage) { + const url = `${BASE_URL}${page.url}`; + return { + "@context": "https://schema.org", + "@type": "TechArticle", + "@id": `${url}#article`, + headline: page.data.title, + description: page.data.description, + image: `${BASE_URL}${ogImageUrl(docsOgImage(page))}`, + ...(page.data.lastModified && { + dateModified: new Date(page.data.lastModified).toISOString(), + }), + publisher: { + "@type": "Organization", + "@id": "https://www.thesys.dev/#organization", + name: "Thesys", + url: "https://www.thesys.dev", + }, + mainEntityOfPage: url, + url, + }; +} + function docsMetadata(page: DocsSourcePage): Metadata { return { - title: page.data.title, + title: page.data.seoTitle ?? page.data.title, description: page.data.description, alternates: { canonical: page.url, diff --git a/docs/components/seo/JsonLd.tsx b/docs/components/seo/JsonLd.tsx new file mode 100644 index 0000000000..7dcda282ee --- /dev/null +++ b/docs/components/seo/JsonLd.tsx @@ -0,0 +1,12 @@ +/** + * Renders a JSON-LD structured-data block, server-rendered so crawlers see it in the initial HTML. + * `<` is escaped so text inside the data can't close the script tag. + */ +export function JsonLd({ data }: { data: Record<string, unknown> | Record<string, unknown>[] }) { + return ( + <script + type="application/ld+json" + dangerouslySetInnerHTML={{ __html: JSON.stringify(data).replaceAll("<", "\\u003c") }} + /> + ); +} diff --git a/docs/content/docs/api-reference/a2ui.mdx b/docs/content/docs/api-reference/a2ui.mdx index 83aaed8d06..d9f5944e6d 100644 --- a/docs/content/docs/api-reference/a2ui.mdx +++ b/docs/content/docs/api-reference/a2ui.mdx @@ -1,5 +1,6 @@ --- title: "@openuidev/a2ui" +seoTitle: "A2UI v1.0 protocol client and React renderer with OpenUI Lang" description: A2UI v1.0 protocol support with OpenUI Lang component updates and an optional React renderer. --- diff --git a/docs/content/docs/openui-lang/comparison.mdx b/docs/content/docs/openui-lang/comparison.mdx index e84c2baf3f..936eabbca0 100644 --- a/docs/content/docs/openui-lang/comparison.mdx +++ b/docs/content/docs/openui-lang/comparison.mdx @@ -1,5 +1,6 @@ --- title: OpenUI vs others +seoTitle: "OpenUI vs A2UI vs json-render vs OpenGenerativeUI" description: OpenUI vs json-render (Vercel), A2UI (Google), and CopilotKit OpenGenerativeUI. --- diff --git a/docs/source.config.ts b/docs/source.config.ts index 0cb2de6c4f..1a093d0af4 100644 --- a/docs/source.config.ts +++ b/docs/source.config.ts @@ -4,6 +4,8 @@ import lastModified from "fumadocs-mdx/plugins/last-modified"; import { z } from "zod"; const docsPageSchema = pageSchema.extend({ + // Search title for <title>; the H1 and sidebar keep `title`. + seoTitle: z.string().optional(), customHeader: z.boolean().optional().default(false), // Hides the previous/next page links at the bottom of the page. hideFooter: z.boolean().optional().default(false), From d3d8ce9ac40c510d141b874d8900dda9bbb805d8 Mon Sep 17 00:00:00 2001 From: zahlekhan <zahle@thesys.dev> Date: Tue, 13 Oct 2026 17:41:42 +0530 Subject: [PATCH 2/2] docs: title the comparison page OpenUI vs A2UI vs json-render vs OpenGenerativeUI Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --- docs/content/docs/openui-lang/comparison.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/content/docs/openui-lang/comparison.mdx b/docs/content/docs/openui-lang/comparison.mdx index 936eabbca0..f1d9f1c9d2 100644 --- a/docs/content/docs/openui-lang/comparison.mdx +++ b/docs/content/docs/openui-lang/comparison.mdx @@ -1,6 +1,5 @@ --- -title: OpenUI vs others -seoTitle: "OpenUI vs A2UI vs json-render vs OpenGenerativeUI" +title: OpenUI vs A2UI vs json-render vs OpenGenerativeUI description: OpenUI vs json-render (Vercel), A2UI (Google), and CopilotKit OpenGenerativeUI. ---