Every integration quickstart in docs/ and showcase/shell-docs/ now opens with
a "Create a free account" step that points the reader at the Enterprise
Intelligence Platform before the framework path. Existing top-of-page
<OpsPlatformCTA> blocks on the six integrations that already had one are
left in place.
- New <SignupLink surface="docs_<int>_quickstart_step1">…</SignupLink> MDX
component in both apps. It mirrors OpsPlatformCTA's URL+UTM contract
(https://dashboard.operations.copilotkit.ai/ with the canonical docs
UTMs, picked up from NEXT_PUBLIC_INTELLIGENCE_SIGNUP_URL when set) and
fires the same PostHog event the other CTAs use:
posthog.capture("try_for_free_clicked", { location: surface }).
- Registered as an MDX global in:
docs/app/integrations/[[...slug]]/page.tsx
docs/app/(home)/[[...slug]]/page.tsx
showcase/shell-docs/src/lib/mdx-registry.tsx
- All 28 integration quickstart .mdx files now lead with a Step that uses
this component as an inline link inside a single sentence of prose —
no CTA card inside <Steps>.
The <TailoredContent> "Choose your starting point" / "How do you want to
get started?" selector is now wrapped in its own <Step> so it advances
the counter, and the inner CLI/manual paths render as steps 3, 4, 5, …
instead of 2, 3, 4, …. Applies to all 20 quickstarts that use the
picker.
- Indigo→purple gradient text on the Step 1 heading on both surfaces
(`.fd-steps > .fd-step:first-child h3` on docs/,
`.docs-steps > div:first-child h3` on shell-docs). Direct-child
combinator scopes it to the outer first Step so inner first-children
inside TailoredContentOption don't pick it up. Bump weight to 700
and font-size to 1.375rem on docs/ to compensate for the
background-clip:text rendering path (grayscale AA, no solid fill)
which makes glyphs look lighter/smaller than the adjacent solid
600/20px headings.
- Tone down the selected TailoredContent option card on both surfaces
to a near-grayscale wash (from-slate-50 → to-indigo-50/30) and
shorten the card itself (smaller padding, smaller icon, smaller
title; extra left padding for breathing room) so the picker takes
less vertical space and doesn't compete with the Step 1 gradient
heading. Indigo ring still does the "selected" signal.
- Bump the tablist's bottom margin in shell-docs (my-2 → mt-2 mb-6)
so the gap between the picker and the first inner Step matches the
1.5rem gap that every other consecutive-Step transition uses.
- Black SignupLink color in Step 1 on shell-docs so the link doesn't
clash with the gradient heading above it.
- Shell-docs: reset margin-top on the first heading inside any Step so
the badge and heading align, and nudge the badge top from -0.125rem
to 0.1875rem so its vertical center matches the heading line center.
Moved the badge's appearance (background/border/color/font-weight)
out of inline style and into globals.css so :first-child overrides
can win without fighting inline-style specificity.
⚠️ **Docs sync — MANUAL REVIEW REQUIRED**
This PR was auto-opened because the docs-sync script detected
showcase-local modifications overlapping with upstream changes.
The script attempted a best-effort 3-way merge:
- Where `git merge-file` produced a clean merge, the merged content was
written.
- Where `git merge-file` produced conflict markers, **upstream content
was written as-is** and showcase-local modifications were overridden.
**Manual review required.**
### Source
- Upstream ref:
[`3552bdd48`](https://github.com/CopilotKit/CopilotKit/commit/3552bdd48)
- Workflow run:
https://github.com/CopilotKit/CopilotKit/actions/runs/25689341730
**Review before merging.** Auto-merge is intentionally disabled for
`needs-review` PRs — confirm the upstream-wins sections preserve any
intentional showcase-local divergence you want to keep, then merge
manually.
---
### Update 2026-05-13 — corrective commit on top
A second commit `8e1d969e` was added by Sam on top of the bot's original
`ad4ea35c` to revert specific changes that conflicted with deliberate
shell-docs decisions (e.g. resurrected deleted landing pages,
`/quickstart` shim revert, EIP brand regression, `react-core/v2` →
`react-core` import-path regression).
**Several of the corrective-revert decisions are being re-evaluated** to
confirm we're not throwing away legitimate content updates
(specifically: `premium/self-hosting.mdx` page collapse to `<SelfHosting
/>`, `shared-state.mdx` line removals, `generative-ui/a2ui.mdx` line
removals, `threads.mdx` `<ThreadsEarlyAccess>` wrapper). The corrective
commit may be adjusted before merge based on that re-evaluation.
The bot's original commit is preserved as the first commit on this
branch. To restore the bot's full original proposal, revert `8e1d969e`.
Re-evaluation of the surgical revert (8e1d969ec) found 4 files where the
upstream sync was the right move and my drop was over-conservative:
1. docs/premium/self-hosting.mdx — collapse 559-line inline content into
<SelfHosting /> shell. Component IS registered (SNIPPET_MAP at
docs-render.tsx:464) and renders the shared snippet, which is
structurally identical (same 23 sections, brand-corrected). The page
was duplicating content the snippet already provides.
2. docs/threads.mdx + snippets/shared/threads/threads.mdx — take bot's
versions (drop the <ThreadsEarlyAccess> wrapper; Threads has been
promoted out of early access upstream) but fix
/reference/v2/hooks/useThreads → /reference/hooks/useThreads
(canonical reference path is src/content/reference/, no /v2/ segment).
3. docs/shared-state.mdx — take bot's IntegrationGrid landing-page form.
The pattern was Tyler's deliberate IA refactor in cc8c94589
(refactor(docs): optimize structure, content and navigability,
2026-02-23) — turning content pages into framework-picker landings —
which shell-docs missed at fork time. Extended exclude list to
["agno", "agent-spec", "spring-ai", "langroid"] since those four
frameworks have no shared-state page; without the addition spring-ai
and langroid would render as broken framework cards.
Not taken (separate decision): docs/generative-ui/a2ui.mdx — bot also
turns this into an IntegrationGrid landing, but 13 of 14 frameworks have
NO a2ui page. Adopting the landing pattern now would produce ~13 broken
cards. Stays as content-rich 108-line orientation page until the
framework-scoped a2ui content exists.
## Summary
Phase 4 validation surfaced 13 broken redirects under the
`/unselected/*` tree. They were dropping users (and SEO equity from
indexed legacy URLs) at the framework-agnostic root pages (e.g.
`/prebuilt-components`) instead of the BIA-scoped equivalents (e.g.
`/built-in-agent/prebuilt-components`).
## Root cause
`next.config.ts` `redirects()` runs at the Next.js routing layer,
**before** middleware. So any rule it matches preempts the
`seo-redirects.ts` catalog. The existing `/unselected/*` catch-all in
`next.config.ts` stripped the prefix (`/unselected/foo` → `/foo`),
regardless of what the seo-redirects catalog specified for BIA-scoped
destinations.
## Changes
`showcase/shell-docs/next.config.ts`:
- `/unselected` (root): destination `/built-in-agent` (was `/`)
- `/unselected/:path*` catch-all: destination `/built-in-agent/:path*`
(was `/:path*`)
- Added 14 explicit slug-rename entries above the catch-all, mirroring
`SUBPATH_RENAMES` in `seo-redirects.ts` (S1–S15, minus S13 which is
handled implicitly):
- `agentic-chat-ui` → `prebuilt-components`
- `use-agent-hook` → `programmatic-control`
- `frontend-actions` → `frontend-tools`
- `vibe-coding-mcp` → `coding-agents`
- `generative-ui/{agentic,render-only}` →
`generative-ui/your-components/display-only`
- `generative-ui/{backend-tools,tool-based}` →
`generative-ui/tool-rendering`
- `generative-ui/frontend-tools` → `frontend-tools`
-
`custom-look-and-feel/{bring-your-own-components,customize-built-in-ui-components,markdown-rendering}`
→ `custom-look-and-feel/slots`
- `guide` → `guides`
- `mcp` → `coding-agents`
The pre-existing per-path entries for
`/unselected/{quickstart,server-tools,mcp-servers,...}` are unchanged —
they already routed correctly to `/built-in-agent/*`. Same for the
`unselected/ag-ui` → `/backend/ag-ui` and `unselected/copilot-runtime` →
`/backend/copilot-runtime` special cases.
## What's NOT changed (intentionally)
- `/unselected/agent-app-context` → `/` kept as-is. The comment in
next.config notes "agent-app-context was concept-per-framework only; no
canonical root home." Genuine product call, not a redirect bug.
- `/copilot-suggestions` → `/` and other non-`/unselected/*`
catalog/next.config conflicts left alone. Those reflect deliberate
product decisions ("orphaned broken stub") that the catalog hasn't
caught up with — separate cleanup.
## Test plan
- [ ] Build succeeds
- [ ] After deploy, re-run Phase 4 redirect catalog probe —
`unselected/*` failures should drop from 13 to 0
- [ ] Manual spot-check: `curl -sIL
https://docs.showcase.copilotkit.ai/unselected/agentic-chat-ui` → final
URL `/built-in-agent/prebuilt-components`, status 200
- [ ] Manual spot-check: `curl -sIL
https://docs.showcase.copilotkit.ai/unselected/some-random-path` →
`/built-in-agent/some-random-path` (catch-all path)
The next.config redirects() block runs at Next.js routing time (before
middleware), so it preempts the seo-redirects.ts catalog rules. The
existing catch-all dropped users at the framework-agnostic root tree
(/agentic-chat-ui, /frontend-tools, etc.) instead of the BIA-scoped
equivalent (/built-in-agent/...), diffusing SEO equity from legacy
/unselected/ URLs.
Changes:
- /unselected (root): destination /built-in-agent (was /)
- /unselected/:path* catch-all: destination /built-in-agent/:path* (was /:path*)
- Add 14 explicit slug-rename entries above the catch-all, mirroring
SUBPATH_RENAMES in seo-redirects.ts (S1-S15 minus S13).
Verified against Phase 4 redirect probe — closes 13 of 22 unselected/
failures.
## Summary
Client-side telemetry on `docs.showcase.copilotkit.ai` was silent. The
shell-docs Dockerfile and `showcase_build.yml` workflow never plumbed
the `NEXT_PUBLIC_*` analytics keys through to `next build`, so the
client JS chunks shipped with empty strings (verified by grepping the
live bundle: `let l = i(95704).env.NEXT_PUBLIC_POSTHOG_KEY` — a runtime
lookup with no inlined value).
Railway runtime env doesn't reach the Docker build phase, so server-side
reads (middleware `POSTHOG_KEY`, server-component canonical URLs) worked
but client-side reads (posthog-js init, RB2B, Scarf, Reo, GA) silently
no-op'd in the browser.
## Changes
- **`showcase/shell-docs/Dockerfile`** — declare `ARG` + `ENV` for
`NEXT_PUBLIC_POSTHOG_KEY`, `NEXT_PUBLIC_RB2B_ID`,
`NEXT_PUBLIC_SCARF_PIXEL_ID`, `NEXT_PUBLIC_REO_KEY`,
`NEXT_PUBLIC_GOOGLE_ANALYTICS_TRACKING_ID` in the builder stage so they
reach `next build`.
- **`.github/workflows/showcase_build.yml`** — add
`build_args_analytics: "yes"` flag to the shell-docs matrix entry;
extend the `Prepare build args` step to emit the five `NEXT_PUBLIC_*`
`--build-arg`s when the flag is set, sourcing values from repo secrets.
Mirrors the existing shell-dashboard pattern (`build_args_pb_url` /
`build_args_shell_url` / `build_args_ops_url`).
## Secrets
Existing repo secret reused: `POSTHOG_PROJECT_KEY`.
New repo secrets required (configured separately in repo settings before
this lands):
- `RB2B_ID`
- `SCARF_PIXEL_ID`
- `REO_PROJECT_KEY`
- `GOOGLE_ANALYTICS_TRACKING_ID`
## Out of scope (intentionally)
- `NEXT_PUBLIC_BASE_URL` is already correctly working via Railway
runtime env (canonical links render with `https://docs.copilotkit.ai`) —
left alone.
- Server-side `POSTHOG_KEY` (no `NEXT_PUBLIC_` prefix) stays on Railway
runtime env; middleware reads it at Edge Runtime.
## Test plan
- [ ] Next build of shell-docs succeeds with new ARGs in scope
- [ ] After deploy, search the live bundle on
`docs.showcase.copilotkit.ai` for the literal `phc_` prefix — must be
present (not `process.env.NEXT_PUBLIC_POSTHOG_KEY` runtime lookup)
- [ ] PostHog Live Events shows `$pageview` (client) and `$autocapture`
arriving from staging
- [ ] RB2B / Scarf / Reo / GA dashboards show events from staging
- [ ] Server-side `seo_redirect` + `docs_pageview` continue firing (no
regression)
The previous commit used `NEXT_PUBLIC_RB2B_ID` based on a stale entry
in the cutover plan doc, but `app/layout.tsx:86` reads
`NEXT_PUBLIC_REB2B_KEY`. Without this fix the build-arg would be
piped under the wrong name and the REB2B Script tag would still not
render.
Client-side telemetry on docs.showcase.copilotkit.ai was silent: the
shell-docs Dockerfile and Showcase Build & Push workflow never plumbed
NEXT_PUBLIC_POSTHOG_KEY / RB2B_ID / SCARF_PIXEL_ID / REO_KEY /
GOOGLE_ANALYTICS_TRACKING_ID through to `next build`. Railway runtime
env doesn't reach the Docker build phase, so the client JS chunks
shipped with empty strings — posthog-js.init etc. silently no-op'd in
the browser.
Mirrors the shell-dashboard pattern: matrix flag triggers the args
block; values come from repo secrets (POSTHOG_PROJECT_KEY already
existed; RB2B_ID, SCARF_PIXEL_ID, REO_PROJECT_KEY,
GOOGLE_ANALYTICS_TRACKING_ID added separately in repo settings).
Server-side telemetry (middleware seo_redirect, docs_pageview) was
unaffected — it reads POSTHOG_KEY at Edge Runtime, which Railway
runtime env satisfies.
## Summary
PR #4691 introduced `import { createPortal } from "react-dom"` in
`src/components/search-trigger.tsx` but did not add `@types/react-dom`
to `showcase/shell-docs/package.json`'s devDependencies. The Railway
production build fails:
```
./src/components/search-trigger.tsx:4:30
Type error: Could not find a declaration file for module 'react-dom'.
'/app/shell-docs/node_modules/react-dom/index.js' implicitly has an 'any' type.
```
Local dev was unaffected because the type was being satisfied via
hoisting from a root `node_modules`. The Docker builder installs each
package's deps in isolation, so the type resolution failed.
## Fix
Add `@types/react-dom: ^19.0.0` to shell-docs devDependencies (matches
the existing `@types/react: ^19.0.0` constraint and resolves to the same
major version as the runtime `react-dom: ^19.0.0`).
## Test plan
- [ ] Railway production build succeeds
- [ ] `npx tsc --noEmit` from `showcase/shell-docs/` returns no
`react-dom` errors
- [ ] No regression in local dev
PR #4691 introduced createPortal from react-dom in search-trigger.tsx
but the shell-docs package was missing @types/react-dom, breaking the
Railway production build with:
Type error: Could not find a declaration file for module 'react-dom'
Hoisting masks this in local dev, but the Docker builder installs
each package's deps in isolation.
The InlineDemo Code tab previously embedded feature-viewer.copilotkit.ai
in an iframe. Feature-viewer only ships six canonical demos for a
limited set of frameworks, so every other (framework x demo) pair —
including the dozen-plus newer demos like frontend-tools, voice,
subagents, gen-ui-interrupt — rendered a 404 or had its Code tab
suppressed entirely.
Add a client-side <DemoSource> component that reads the same
demo-content.json bundle <Snippet> already consumes, scoped to one
(integration, demo) cell. By default it shows only files flagged in the
manifest's `highlight:` array, sorted by the new `highlightOrder` field
so tabs render in author-defined order. Falls back to all bundled files
when nothing is flagged. Rendering matches <Snippet>'s look (same hljs
classes, CopyButton, border / type scale) for visual continuity.
Wire <DemoSource> into the InlineDemo Code tab and remove the
feature-viewer URL construction. The base import of getDocsFolder is
dropped from mdx-registry.tsx since it was only used for the iframe
URL; getDocsFolder remains in registry.ts for the framework routing
layer that still depends on it.
## Summary
Ten commits porting `docs.copilotkit.ai`'s visual baseline + page
architecture onto shell-docs ahead of the May 12 cutover.
This is a **visual-replica port**, not an IA change — the docs
information architecture stays as shipped (sidebar groupings, JTBD
section names, BIA-as-default behavior). What changes is the visual
layer: colors, typography, sidebar/navbar/TOC chrome, page layout
architecture, banner, content-column geometry, search modal, dark mode,
and docs page chrome polish.
## Commits
1. **Visual baseline** — color tokens (accent, glass-background, bg),
typography (Plus Jakarta Sans + system mono), callouts (white card +
colored left strip + lucide icons), tables (row-bottom borders),
code-block chrome (`rounded-xl` + `shadow-sm`), `.docs-content-wrapper`
rule (white panel + left-edge fade).
2. **Page layout architecture** — replicates canonical's fixed-height
body + internal scroll on `.docs-content-wrapper`. Banner + navbar +
sidebar are naturally at the top/left of body (no sticky positioning).
TOC moves inside the content wrapper. Route shells normalized to `h-full
w-full` with explicit scroll wrappers.
3. **Two-piece navbar** — slanted SVG separator between left brand panel
and right utility cluster, glass-panel chrome, three-zone search trigger
(icon + label + ⌘K), per-link underline cross-fade animation, brand
assets (kite mark + slanted borders + theme icons).
4. **Dismissable top promo banner** — `<Banners />` component with
localStorage TTL, lucide rocket icon, `id` namespacing.
5. **Right-rail TOC** — direct port of fumadocs-ui's clerk pattern:
persistent gray vertical guides at depth-specific offsets + diagonal SVG
connectors at H2↔H3 transitions + a violet thumb that paints only along
the line path via SVG mask. Active text turns violet, inactive at 60%
opacity.
6. **Sidebar pill + content alignment** — adds `pr-1` to the sidebar's
inner scroll container so the active-link pill stops 4px short of the
scrollbar (matches canonical). Replaces the content column's `px-8 py-6
xl:px-16 xl:py-12` + non-centered `max-w-[900px]` with canonical's exact
`px-4 py-6 md:px-6 md:pt-8 xl:px-8 xl:pt-14` outside, `max-w-[900px]
mx-auto` inside, so the column centers between sidebar and TOC and the
h1 lands at the same x as `docs.copilotkit.ai` at 1440.
7. **Search modal portal** — the modal renders inside SearchTrigger,
which lives in the navbar's right cluster. `backdrop-filter` creates a
containing block for fixed-position descendants, which was clamping the
modal's `fixed inset-0` overlay to the cluster (~505x70px) instead of
the viewport. Wraps SearchModalWrapper in `createPortal` mounted on
`document.body` so the overlay covers the page.
8. **Dark mode parity** — adds `.dark` token block to `globals.css`
mirroring canonical for every var the wave3 chrome consumes;
`@custom-variant dark (&:is(.dark *))` so `dark:` Tailwind utilities
react to the `.dark` class instead of `prefers-color-scheme`; inline
`beforeInteractive` script that reads `localStorage.theme` (falling back
to `prefers-color-scheme`) and applies the class before first paint with
`suppressHydrationWarning` on `<html>` so Next.js doesn't revert it;
themed thin scrollbar on `.docs-content-wrapper` and the sidebar's inner
scroll container; rounded hover surface on the navbar's
GitHub/Discord/theme icon buttons.
9. **TOC scrollspy at scroll bottom** — replaces IntersectionObserver
with a scroll listener on `.docs-content-wrapper` that walks the heading
list and forces the final heading active when the scroll container is at
`scrollMax`. The previous observer never fired for the last heading once
it had scrolled past the rootMargin band.
10. **Docs page chrome polish** — tightens the page header → body gap
from `mb-14` (responsive) to a flat `mb-8` matching canonical; section
header banner sized at 15px with same Plus Jakarta default-weight
uppercase + tracking as canonical's separator, sized up; `--border`
(instead of `--border-dim`) on the section divider rule so it survives
dark; active-link pill gets a `--bg-hover` surface + 1px white/10 ring
in dark for contrast; idle pages get `dark:hover:bg-white/5`; nested
section separators (depth > 0) demote to 11px `--text-faint` labels with
no divider rule (no more shouting at the same hierarchy as the parent
banner); root `/docs` overview moves onto the same SidebarNav +
`.docs-content-wrapper` + `max-w-[900px] mx-auto` shell the per-doc
routes use; rename the "Give Your App Agent Powers" main-meta section to
"Adding Agent Powers".
## Verification
- `npm run build` clean.
- 1440x900 Playwright at scroll 0 vs scrollMax: nav
`getBoundingClientRect()` delta = `{top: 0, left: 0, width: 0}` — zero
drift.
- 1440x900 Playwright vs `docs.copilotkit.ai`: aside, wrapper, and h1.x
coordinates match canonical exactly; active-link pill has the same gap
to the scroll gutter as canonical.
- Search modal opens as a full-viewport overlay (1440x900 backdrop,
centered card) instead of a clipped strip under the trigger.
- Dark mode round-trips light → click toggle → `html.dark` +
`localStorage.theme="dark"` → click again → light. Sidebar, navbar
(slanted-dark borders + theme moon icon), banner, content panel,
callouts, code, tables, TOC, scrollbar, and search modal all paint
correctly in dark; no light-flash on dark-preferring first loads.
- TOC last heading activates at scroll bottom on all docs pages.
## Out of scope
- IA changes (sidebar groupings, navbar destinations, content) — content
stays as shipped.
## Test plan
- [ ] Visual review at 1440x900 against `docs.copilotkit.ai`: sidebar
pinning, navbar pinning, TOC outline + sliding violet, content-panel
left-edge gradient, content column centering, sidebar section header
treatment
- [ ] Active-link pill in the docs sidebar has visible gap to the
scrollbar in both light and dark
- [ ] Cmd/Ctrl+K opens the search modal as a full-viewport overlay;
click backdrop or Escape closes
- [ ] Toggle theme button switches light↔dark, persists across reload,
no light-flash on first load
- [ ] Scroll a long page (e.g. `/built-in-agent/concepts/architecture`)
— confirm last TOC heading activates at the bottom
- [ ] Mobile (< 1280px) — TOC hides, sidebar collapses to mobile menu
The framework selector pill in the sidebar tinted its 40px icon tile
with bg-[var(--accent)]/25 when a framework was active. In light mode
that paints as soft lavender against the lavender pill -- the
canonical look. In dark mode it paints as a dark muted purple, and
the CopilotKit kite (which is itself purple-toned) blends straight
into it -- the brand mark essentially disappears.
Add dark:bg-white/10 so the active tile flips to a neutral elevated
surface in dark mode. Light mode keeps the lavender. The kite stands
out against white/10, and the other framework brand marks (Mastra,
LangGraph, CrewAI, etc.) all read cleanly against the neutral too --
no framework loses contrast in the swap.
The framework-scoped routes had two more places still rendering against
the pre-wave3 shell that the root /docs page just got migrated off of:
- FrameworkLandingPage (e.g. /mastra, /langgraph-python) used the old
240px sidebar with p-4 + bg-[var(--bg)] + browser-default scrollbar,
plus a max-w-4xl content column with no centering.
- NotAvailableForFrameworkPage (rendered when a slug exists for some
frameworks but not the URL's) used the same old shell.
Move both onto the SidebarNav + .docs-content-wrapper +
max-w-[900px] mx-auto pattern docs-page-view uses, and update RenderNav
to match the new section/page/group treatment from OverviewNavItem
(15px banner sections at depth 0, 11px text-faint demoted labels at
depth > 0, h-10 rounded-lg pill page links with dark-aware hover,
border-l tree on nested groups). Picking a different framework now
produces the same chrome as the root overview, instead of revealing
the old shell.
Tighten the gap between the page header and the body to canonical's
mb-8 (was mb-14 with a responsive ladder that opened a half-inch hole
at xl widths between the description paragraph and the first prose
paragraph).
Align the sidebar treatment to canonical:
- Section header banner uses the same Plus Jakarta default-weight
uppercase + tracking as canonical's separator, sized up to 15px so
it sits above the link list as a clear divider rather than a tiny
caption below it.
- Section divider rule uses --border (white/10 in dark, #d9d9e0 in
light) so the horizontal line survives the dark token set.
- Active-link pill picks up dark-mode contrast: --bg-hover surface
with a 1px white/10 ring instead of the near-flat --bg-surface
that read as undifferentiated against the sidebar bg in dark.
- Idle pages get a white/5 hover affordance in dark.
- Nested section separators (depth > 0) demote to a quiet 11px
uppercase label in --text-faint with no divider rule, so the
Build Generative UI > Controlled / Declarative / Open-Ended
subsection breaks read as inline labels inside their parent
group instead of competing banner headers.
Move the root /docs overview route onto the same SidebarNav +
.docs-content-wrapper + max-w-[900px] mx-auto pattern docs-page-view
uses, and rewrite OverviewNavItem so sections, pages, and groups
paint with the same tokens as the per-doc routes -- previously the
root was still rendering against the pre-wave3 shell with a 240px
sidebar, p-4 padding, browser-default scrollbar, and an off-center
max-w-4xl content column.
Rename the "Give Your App Agent Powers" main-meta section to
"Adding Agent Powers" per product copy.
The TOC scrollspy used IntersectionObserver with a -20%/-70% root
margin band, which never fires for the last heading once the user
has scrolled it past that band. The active state stayed parked on
whichever heading last entered the band even when the user had
clearly arrived at the document end.
Replace with a scroll listener on .docs-content-wrapper that walks
the heading list, picks the last heading whose top has crossed the
trigger line (~25% from the viewport top), and forces the final
heading active when the scroll container is at scrollMax. Falls back
to window scroll for routes that don't wrap content in
.docs-content-wrapper.
Add a .dark token block to globals.css mirroring canonical
docs.copilotkit.ai for every var the wave3 chrome consumes (--bg,
--bg-surface, --bg-elevated, --bg-hover, --border, --border-dim,
--sidebar, --glass-background, --text*, --accent*, --violet*, --blue,
--scrollbar-color, --scrollbar-track). The navbar already shipped a
Toggle theme button + sun/moon SVGs + documentElement.classList
.toggle('dark') + localStorage.theme persistence; this commit makes
that toggle paint the rest of the page because all wave3 chrome
(sidebar, navbar, banner, callouts, tables, TOC, content wrapper)
consumes those vars.
Add @custom-variant dark (&:is(.dark *)) so dark: Tailwind utilities
react to the .dark class instead of prefers-color-scheme. Without
this, the navbar's class-driven swap pairs (slanted-end-border-dark
vs -light, theme-moon vs theme-sun, every dark: utility) are dead
when the theme toggle runs.
Add an inline beforeInteractive script in <head> that reads
localStorage.theme (falling back to prefers-color-scheme) and applies
the class before first paint, with suppressHydrationWarning on <html>
so Next.js doesn't revert the class to match the server output.
Apply scrollbar-width: thin and scrollbar-color to .docs-content-wrapper
and the sidebar's inner scroll container so the thumb tracks the
active theme instead of paying the bright browser default that pops
against dark surfaces.
Bump dark-mode contrast on the icon-button hover surface in the
navbar (GitHub, Discord, theme toggle) to a rounded black/5 in light
and white/10 in dark so the buttons read as interactive.
The search modal renders inside SearchTrigger, which lives in the
navbar's right cluster. The cluster has backdrop-blur-lg, and
backdrop-filter creates a containing block for fixed-position
descendants. The modal's `fixed inset-0` overlay was therefore being
clamped to the cluster's bounding rect (~505x70 at 1440x900) instead
of the viewport, so the overlay+card rendered as a tiny clipped strip
under the search button.
Wrap SearchModalWrapper in createPortal mounted on document.body. The
modal now resolves position: fixed against the viewport like a normal
overlay.
The 53 hard-500 URLs in the public sitemap split into three independent
root causes, all surfaced in production-mode rendering only because the
underlying issues throw inside next-mdx-remote:
1. tutorials/ai-powered-textarea/step-2-setup-copilotkit and
tutorials/ai-todo-app/step-2-setup-copilotkit reference
<CopilotCloudConfigureCopilotKit>,
<SelfHostingCopilotRuntimeConfigureCopilotKit>, and
crewai-flows/quickstart references <CloudCopilotKit> — three
unsuffixed component names whose only registered counterparts in
docsComponents end in "Provider". MDX rendering throws "Expected
component X to be defined" and 500s. Adds the unsuffixed names as
aliases of the existing Provider stubs in mdx-registry.
deploy-agentcore (langgraph variants + aws-strands) uses
<Content framework="..." />, also unregistered. Adds Content as a
children-passthrough stub for the same reason.
2. Three langgraph tutorial pages (agent-native-app/step-6-shared-state,
ai-travel-app/step-3-setup-copilotkit,
ai-travel-app/step-4-integrate-the-agent) place a closing </Step>
tag immediately under a markdown bullet list with no blank line
separator. The remark parser treats the closing tag as a list-item
continuation, errors out with "Expected the closing tag </Step>",
and ships a 500. Adds the missing blank line before the close tag.
3. Five MDX files (llamaindex + adk shared-state/predictive-state-updates,
pydantic-ai shared-state/in-app-agent-write, plus crewai-flows and
pydantic-ai human-in-the-loop/index — last two not in the 53 but
share the bug) use {/\\* ... \\*/} where the asterisks are escaped.
Acorn cannot parse the resulting expression and the page 500s.
Replaces the escapes with proper {/* ... */} JSX comments.
Verified: a clean production build of shell-docs followed by
`npx next start` and a curl probe of all 53 URLs from
validation/test2_results.json returns 200 across the full set with
zero MDX or React errors in the server log.
Two issues found in Phase 4 validation against docs.showcase.copilotkit.ai.
1. 31 entries in seo-redirects.ts had source === destination, causing
middleware to issue infinite 301 loops on canonical URLs like
/frontend-tools, /faq, /human-in-the-loop. Remove the dead entries
and add a defense-in-depth skip-when-equal guard in middleware so
future drift cannot regress.
2. Framework-scoped paths (e.g. /agno/frontend-actions) bypassed the
redirect catalog entirely because the pathIsFrameworkScoped short-
circuit fired before any catalog lookup. Reorder middleware so the
exact-match catalog is consulted first for every request, including
framework-scoped paths. Wildcard scan still skips legacy patterns
that would hijack canonical framework URLs, but allows same-framework
wildcard rewrites (e.g. /agno/concepts/:path* -> /agno) to fire.
85 framework-scoped redirects across 18 registry slugs now resolve
instead of soft-404ing.
Verified by curl probes against localhost: all 85 framework-scoped
catalog entries 301 to the expected destination, all 31 former self-
loop URLs return 200, and canonical framework URLs (/agno, /langgraph-
python/quickstart, etc.) still pass through unchanged.
The InlineDemo Code tab constructs a feature-viewer.copilotkit.ai URL
from the integration's docs-folder name, but feature-viewer expects its
own slug scheme. Six framework slugs 404'd outright (built-in-agent,
google-adk, claude-sdk-python, claude-sdk-typescript, ms-agent-python,
ms-agent-dotnet) and two more were named differently (crewai-crews
needed crewai, llamaindex needed llama-index). Demo IDs also diverged
(gen_ui_tool_based vs tool_based_generative_ui, hitl_in_chat vs
human_in_the_loop, etc.) so even when the framework slug was right the
Code panel rendered an empty 404 page.
Add getFeatureViewerSlug() and getFeatureViewerDemoId() to registry.ts
with explicit override maps. Both return null when the integration or
demo has no feature-viewer counterpart. Update mdx-registry.tsx to use
both and to suppress the Code tab (rendering the Demo iframe alone)
whenever either helper returns null.
Verified by probing feature-viewer.copilotkit.ai for each (framework x
demo) combination using NEXT_HTTP_ERROR_FALLBACK soft-404 detection plus
inspection of the rendered code panel; all post-fix URLs that the
helpers emit resolve to real code panels for the six demos
feature-viewer ships (agentic_chat, tool_based_generative_ui,
agentic_generative_ui, predictive_state_updates, shared_state,
human_in_the_loop).
The active-link pill in the docs sidebar rendered flush against the
scroll gutter because the inner scroll container had no right padding;
canonical adds pr-1 on its scroll container so the pill stops 4px short
of the scrollbar. Match that.
The content column used px-8 py-6 xl:px-16 xl:py-12 with a non-centered
max-w-[900px] cap, which pushed content 32px to the right of canonical
at xl widths and left a wide gap between content and the right rail.
Replace with canonical's exact pattern: px-4 py-6 md:px-6 md:pt-8
xl:px-8 xl:pt-14 outside, max-w-[900px] mx-auto inside, so the column
centers between sidebar and TOC and the h1 lands at the same x as
docs.copilotkit.ai at 1440.
Updates the docs navbar and showcase shell-docs brand-nav so the "Talk
to engineers" CTA points at copilotkit.ai/talk-to-an-engineer instead
of the deprecated /contact-us endpoint.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Phase 3 of the docs.copilotkit.ai cutover. Surfaces a complete sitemap,
basic robots config, and per-framework self-canonical metadata so every
URL variant is indexed under its own canonical instead of collapsing
onto a single root.
- **sitemap.ts** — emits one entry per (root URL × framework variant)
pair plus reference, AG-UI, and per-framework override pages.
`lastModified` resolves from MDX frontmatter \`lastmod\` first, then
file mtime, then \`new Date()\`. Strips Next.js route-group \`(name)\`
segments and trailing \`/index\` so URLs match what the routers actually
serve. ~2,250 entries.
- **robots.ts** — allow all, disallow \`/api/\`, sitemap pointer at
\`\${NEXT_PUBLIC_BASE_URL}/sitemap.xml\`.
- **sitemap-helpers.ts** — shared MDX walking + base-URL resolution.
- **generateMetadata()** added to the four catch-all docs routes
(\`[[...slug]]\`, \`[framework]/[[...slug]]\`, \`reference/[...slug]\`,
\`ag-ui/[[...slug]]\`). Each sets \`alternates.canonical\` to the page's
own full URL — per-framework self-canonical, not root canonical. So
\`/langgraph-python/quickstart\` declares itself canonical,
\`/agno/quickstart\` declares itself canonical, and the bare
\`/quickstart\` declares itself canonical too.
- **.env.example** — documents \`NEXT_PUBLIC_BASE_URL\` and
\`NEXT_PUBLIC_SHELL_URL\`. Extended the existing comment in
\`next.config.ts\` with the new consumers.
## Test plan
- [x] \`npm run build\` from \`showcase/shell-docs/\` passes; route
table shows \`/sitemap.xml\` and \`/robots.txt\` as static.
- [x] \`curl http://localhost:3099/sitemap.xml\` returns valid XML;
2,254 \`<url>\` entries covering bare unscoped, framework-scoped,
reference, and AG-UI URLs; no \`(other)\` route-group leakage; no
trailing \`/index\` artifacts.
- [x] \`curl http://localhost:3099/robots.txt\` returns the expected
User-Agent / Allow / Disallow / Sitemap config.
- [x] \`/shared-state\` HTML contains \`<link rel="canonical"
href=".../shared-state">\`.
- [x] \`/langgraph-python/quickstart\` HTML contains \`<link
rel="canonical" href=".../langgraph-python/quickstart">\` (NOT pointing
at the bare \`/quickstart\`).
- [x] \`/agno/quickstart\` and \`/reference/components/CopilotChat\` and
\`/ag-ui/concepts/architecture\` all self-canonical.
## Summary
- Updates the V1 reference autogen entry in `scripts/docs/lib/files.ts`
for the upstream rename of `sdk-python/copilotkit/langgraph_agent.py` to
`langgraph_agui_agent.py`. The class also renamed from `LangGraphAgent`
to `LangGraphAGUIAgent` (now subclasses the upstream `LangGraphAgent`).
- Updates `sourcePath`, `destinationPath` (now
`LangGraphAGUIAgent.mdx`), `title`, `description`, and `pythonSymbols`
to match.
- Regenerates
`showcase/shell-docs/src/content/reference/sdk/python/LangGraphAGUIAgent.mdx`.
The autogen run now reports 26/26 entries succeeding (was 25/26 — the
previous entry failed silently via `Promise.allSettled` because the
source file no longer existed).
Stacked on top of #4693 (autogen-retarget). Once that lands this PR can
be retargeted at `main`.
## Test plan
- [x] `tsx scripts/docs/gen.ts` from repo root succeeds with `All
reference docs processed (26/26 succeeded)`.
- [x] `npm run build` in `showcase/shell-docs/` clean (Next build
prerendered 27/27 routes).
- [ ] Spot-check rendered `/reference/sdk/python/LangGraphAGUIAgent` on
a dev server. Note: the new `LangGraphAGUIAgent` class in `sdk-python`
does not yet carry a class-level docstring, so the generated MDX is
currently frontmatter-only. Adding a docstring upstream will populate
the page on the next pipeline run.
## Summary
Two commits porting the V1 reference autogen pipeline so it writes into
shell-docs instead of the legacy upstream tree. Without this,
`/reference/v1/*` URLs (around 20k pageviews per quarter on
`docs.copilotkit.ai`) would 404 or 301 to a stub on shell-docs after the
cutover.
- `c30613c8d` Retarget the autogen pipeline. `scripts/docs/gen.ts` is
the entrypoint; `scripts/docs/lib/files.ts` carries the `REFERENCE_DOCS`
array with hardcoded `destinationPath` strings. Switched all
destinationPath strings from `docs/content/docs/reference/v1/...` to
`showcase/shell-docs/src/content/reference/v1/...`. Routing was verified
via `src/app/reference/[...slug]/page.tsx` (which reads from
`src/content/reference/`, not the legacy `content/docs/reference/`
tree). Anchored cwd to repo root in `gen.ts` to fix a latent
path-resolution bug that had broken the pipeline upstream too. Switched
the orchestration to `Promise.allSettled` so one source-rename rot
doesn't abort the rest.
- `55e72eeec` Lefthook auto-format pass.
## What this generates
25 of 26 V1 reference docs land in
`showcase/shell-docs/src/content/reference/v1/`:
- 20 under `reference/v1/{components,hooks,classes}` (TypeScript-source
autogen)
- 5 under `reference/v1/sdk/python/` (Python-source autogen)
The 26th entry (`sdk-python/copilotkit/langgraph_agent.py` to
`langgraph_agui_agent.py` rename) is tracked separately as a follow-up.
## Test plan
- [ ] `npm run build` from `showcase/shell-docs/` clean.
- [ ] Visit `/reference/v1/hooks/useCopilotAction`,
`/reference/v1/classes/CopilotRuntime`,
`/reference/v1/components/chat/CopilotChat`,
`/reference/v1/sdk/python/LangGraph` on the dev server. Confirm full
content renders.
- [ ] Re-run `tsx scripts/docs/gen.ts` on a clean checkout. Confirm
25/26 entries succeed; the LangGraph SDK Python entry fails gracefully
without aborting the rest.
## Timing note
Merging this pre-cutover means upstream's
`docs/content/docs/reference/v1/*` MDX files freeze at their current
state. If anyone re-runs autogen between now and 5/12 the regenerated
MDX goes to shell-docs only. V1 surface is stable so the practical risk
is small, but the safest move is to hold this PR until cutover day. Open
question for review.
## Summary
Two-part redirect work for the docs.copilotkit.ai → shell-docs cutover,
in one PR.
### Part 1 — Retarget destinations in `seo-redirects.ts`
shell-docs serves canonical framework docs at `/{fw-slug}/...` from the
host root (no `/docs/` prefix) and uses different framework slugs from
the legacy SHELL surface. The redirect catalogue has been retargeted
accordingly:
- **Drop the `/docs/integrations/` prefix** from every destination.
- **Apply framework-slug renames** in destinations:
- `langgraph` → `langgraph-python`
- `adk` → `google-adk`
- `aws-strands` → `strands`
- `microsoft-agent-framework` → `ms-agent-dotnet`
- `crewai-flows` → `crewai-crews`
- **Re-flip the BIA → unselected rename** — `unselected/` was retired;
destinations now point at `/built-in-agent/`.
- **Slug-rename catch-alls** for the bare `/{old-slug}/*` form so legacy
upstream URLs (e.g. `/langgraph/quickstart`) 301 directly to the new
slug.
- **`/docs/integrations/*` and `/docs/*` catch-alls** so any URL still
carrying the legacy SHELL routing prefix lands at the shell-docs
equivalent.
- **`/migration-guides/*` → `/migrate/*`** (4 URLs).
- **Folder-index redirects** for shell-docs folders without an
`index.mdx` (`/troubleshooting`, `/migrate`, `/premium`, `/concepts`,
`/reference`).
390 redirect entries total in the new catalogue.
### Part 2 — Port middleware to shell-docs
- Copied the retargeted `seo-redirects.ts` to
`showcase/shell-docs/src/lib/`.
- Merged the SHELL redirect-middleware logic into shell-docs's existing
pageview-tracking middleware. Redirects fire first (with the
`seo_redirect` PostHog event); non-redirected requests still get the
`docs_pageview` capture and `distinct_id` cookie.
- Preserved the framework-scoped short-circuit so canonical
`/{fw-slug}/...` URLs are never hijacked by legacy patterns.
- Left the SHELL versions of `middleware.ts` and `seo-redirects.ts` in
place — the SHELL still serves `docs.showcase.copilotkit.ai` until DNS
flips.
## Test plan
- [x] `npm run build` clean in `showcase/shell-docs/`
- [x] `npm run build` clean in `showcase/shell/` (existing operation
unaffected)
- [x] `curl -sI
http://localhost:3099/docs/integrations/langgraph/quickstart` → 301 to
`/langgraph-python/quickstart`
- [x] `curl -sI http://localhost:3099/langgraph/quickstart` → 301 to
`/langgraph-python/quickstart`; `/aws-strands/quickstart` →
`/strands/quickstart`; `/migration-guides/v2` → `/migrate/v2`;
`/troubleshooting` → `/troubleshooting/common-issues`; `/coagents` →
`/langgraph-python`
- [ ] Validate full set against a running shell-docs instance with
`validate-redirects.ts` (run from `showcase/scripts/` against the
deployed preview)
R15 and R17 sources were /builtin-agent (no hyphen), matching no real traffic. Real legacy URLs live under /integrations/built-in-agent/* (47 URLs in the upstream sitemap). Retarget the source patterns to match.
Adds a Callout linking to "Build Interactive Agents with Generative UI"
(free DeepLearning.AI short course) on the two Generative UI overview
pages and the six lesson-specific pattern pages (display-only,
tool-rendering, state-rendering, open-generative-ui, a2ui, mcp-apps),
mirrored across docs/ and showcase/shell-docs/.
The earlier V2 canonical sweep misread the brand-name guidance ("always
CopilotKit") as a directive on import paths and stripped /v2 from
@copilotkit/react-core specifiers. Inline review feedback clarified that
guidance applied only to the component name. This restores
"@copilotkit/react-core/v2" and "@copilotkit/react-core/v2/styles.css"
across the docs sweep scope (now including 8 framework quickstarts
inherited via rebase onto main); the <CopilotKit> rename and the drop
of @copilotkit/react-ui from install commands are kept.
Registry demo slugs are dash-form (`agentic-chat`); feature-viewer.copilotkit.ai
serves them at underscore-form (`agentic_chat`). The Code tab iframe was
404'ing because the slug was passed through verbatim. Replace `-` with `_`
when building the code URL.
Wraps the InlineDemo iframe in a Demo / Code tab strip mirroring the
IframeSwitcher component, so pages using <InlineDemo demo="..." /> now
expose both the live demo (integration backend) and a code view. The
code iframe points at feature-viewer.copilotkit.ai/<framework>/feature/
<demo>?view=code&sidebar=false&codeLayout=tabs, where <framework> is
translated through getDocsFolder() to map registry slugs like
langgraph-python down to their upstream folder name (langgraph) used by
the feature viewer.
The mdx-registry shipped a stub IframeSwitcher that took `src`/`title`
props and rendered a single iframe. MDX consumers actually pass
`exampleUrl`/`codeUrl`/`exampleLabel`/`codeLabel`, so the stub was
silently dropping those props and rendering an empty container. The
result: pages using `<IframeSwitcher>` showed no demo+code tabs.
Replace the stub with the real component from `@/components/content`,
which matches the props shape consumers actually use.
IframeSwitcher: forward the `id` prop to a wrapping `<div id={id}>` so MDX
authors can deep-link to a specific switcher instance. The prop was
declared but never consumed.
oss-vs-enterprise.mdx: fix the Frontend SDK bullet to reflect canonical
V2 — both hooks and prebuilt components ship from `@copilotkit/react-core`
(the standalone `@copilotkit/react-ui` package is V1-era).
Several MDX files import `IframeSwitcher` from `@/components/content`
(prebuilt-components, frontend-tools, interactive, tool-rendering, plus
integration overrides), but the component file was missing. This adds it
as a thin wrapper around the existing `<Tabs>` component, rendering a
demo iframe and a code iframe in a Tabs strip.
Props: `exampleUrl`, `codeUrl`, `exampleLabel` (default "Demo"),
`codeLabel` (default "Code"), `height` (default "600px"). Both iframes
are sandboxed and lazy-loaded.
Matches the upstream IframeSwitcher pattern used on docs.copilotkit.ai
to render embedded feature-viewer demos alongside their backing code.
backend/custom-agent.mdx (508 lines, structurally complete) is the canonical
Factory Mode page. The integrations/built-in-agent/custom-agent.mdx copy
(240 lines, missing 5 sections) was retired:
- Add 301 redirects in next.config.ts for the two historical paths
(/built-in-agent/custom-agent and /integrations/built-in-agent/custom-agent
→ /backend/custom-agent).
- Inbound link retargets to /backend/custom-agent landed in the previous
V2 normalization commit (5 files).
- Delete the divergent 240-line copy.
Apply the canonical V2 import form across all V2 docs:
- `<CopilotKit>` (not `<CopilotKitProvider>`)
- imports from `@copilotkit/react-core` (root, not `/v2`)
- styles from `@copilotkit/react-core/styles.css` (not `react-ui/v2/styles.css`)
- drop `@copilotkit/react-ui` from npm install commands
Fix V2 leaks in canonical pages: replace stale `useCopilotAction` and
`useCopilotReadable` references in agentic-protocols/a2a.mdx and
backend/custom-agent.mdx with their V2 equivalents (`useFrontendTool`,
`useAgentContext`).
Excludes intentional V1 references in migrate guides, V1 reference tree,
migration callouts, and V1 release notes.