mirror of
https://github.com/vercel/chat.git
synced 2026-09-14 18:32:29 +08:00
79227ae991
## Summary
Refreshes the adapter docs end-to-end so every adapter — official,
vendor-official, and community — now ships hand-authored MDX, lives
under a clean URL structure, and renders on a polished
sidebar/right-rail layout dedicated to `/adapters` (the shared `/docs`
chrome is untouched).
```mermaid
flowchart LR
subgraph Before
direction TB
OB[official] --> CB[community<br/>incl. 5 vendor pages]
end
subgraph After
direction TB
OA[official] --> VA[vendor-official<br/>5 pages] --> CA[community]
end
Before -.-> After
```
### Content & routing
- **New `/adapters/vendor-official/<slug>` route** for vendor-maintained
adapters (Beeper Matrix, Photon iMessage, Liveblocks, Resend, Zernio).
Sidebar gets a third labelled group ("Vendor-Official Adapters") between
Official and Community, with a top divider matching the existing
Community treatment.
- **All 13 vendor-official + community adapters migrated** from runtime
README fetching to hand-authored MDX with rich `features:` matrices and
full body content (install, quick start, configuration, auth,
gateway/streaming, troubleshooting). README fetch stays as a fallback
for any future community adapter that hasn't been migrated yet, gated by
a new `mdxBody: true` frontmatter flag.
- **Messenger filter pages removed** (`/adapters/for/<messenger>` + the
"Browse by messenger" chip row on `/adapters`). Existing URLs
308-redirect to `/adapters`.
- **Permanent redirects** from
`/adapters/community/{matrix,imessage,resend,zernio,liveblocks}` to
their new `/adapters/vendor-official/...` paths.
- **Fixed** `/docs/adapters` and `/docs/state` so the bare pages are
accessible again — the previous catch-all redirect (`:slug*`) was
swallowing them. Switched to `:slug+` so subpath URLs still 308 while
the bare pages render.
### Visual polish
- **Adapter-only sidebar variant** (`AdaptersDocsLayout` +
`AdaptersSidebar`) with uppercase eyebrow separators, tighter rows, and
a thin themed scrollbar utility class. The shared `/docs` sidebar is
untouched.
- **Restyled `AdapterHero`**: drops the badges row + packageName, sits
the title inline with the logo, larger 17 px tagline, horizontal divider
beneath the block.
- **Restyled `PackageInstall`** as a tabbed dark single-line snippet
with a `$` prompt prefix and a copy button — replaces the previous
multi-line `CodeBlock` layout.
- **New "Deploy your chat app on Vercel" upsell card** (`<Upsell />`)
replaces the old `EditSource / ScrollTop / Feedback / CopyPage` footer
cluster on every adapter detail page.
- **Listing & messenger pages**: align the H1 to a tighter `text-4xl
sm:text-[44px]`, and the section headers to `text-base font-medium
tracking-tight` with a one-line muted lede.
### Tooling & tests
- Added `mdxBody: true` opt-in to the adapter frontmatter schema
(`source.config.ts`), and updated both detail-page handlers
(`community/[slug]` and the new `vendor-official/[slug]`) to render the
MDX body when present, falling back to README fetch otherwise.
- Refactored both detail-page handlers to flatten the body-render
branches into a `renderBody()` helper, removing the nested ternaries
that were tripping `lint/style/noNestedTernary`.
- New test file
[`packages/integration-tests/src/docs-adapters.test.ts`](https://github.com/vercel/chat/blob/docs/refresh-adapters/packages/integration-tests/src/docs-adapters.test.ts)
— **220 new assertions** covering:
- Adapter MDX frontmatter completeness, slug ↔ filename consistency, and
`type ∈ {platform, state}`.
- Vendor-official invariants: exactly the expected slugs,
`vendorOfficial: true`, `community: true`, `author`, `mdxBody: true`,
`<FeatureSupport />` rendered.
- Community invariants: `community: true` (never vendor-official),
`mdxBody: true`, `<FeatureSupport />`.
- Official invariants: never flagged, `packageName` always under
`@chat-adapter/*`.
- `adapters.json` ↔ MDX sync on `packageName` / `type` / `community` /
`vendorOfficial`.
- Extended `VALID_DOC_PACKAGES` so `docs-content.test.ts` accepts the
new vendor-official + community packages, plus `@chat-adapter/web`,
`@chat-adapter/web/react`, and `@chat-adapter/messenger`.
### Per-package AGENTS.md
- Added `AGENTS.md` to every official adapter and state adapter (14
packages), each tailored to that adapter's surface — overview, directory
layout, build/test commands, public exports, thread ID format, webhook
flow, authentication, format conversion, cards/streaming, platform
quirks, testing approach, coding conventions, and release rules.
- Added a one-line `CLAUDE.md` (`@AGENTS.md`) beside each so Claude Code
picks up the same instructions through its built-in resolver — same
convention as the root.
### Web adapter copy
- Cleaned up the Web adapter tagline (removed inline backticks) and
dropped the now-redundant "v1 scope" section from the body.
---------
Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com>
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
import { useCallback, useState } from "react";
|
|
import { toast } from "sonner";
|
|
import {
|
|
Tabs,
|
|
TabsContent,
|
|
TabsList,
|
|
TabsTrigger,
|
|
} from "fumadocs-ui/components/tabs.unstyled";
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"] as const;
|
|
type PackageManager = (typeof PACKAGE_MANAGERS)[number];
|
|
|
|
const buildCommand = (
|
|
manager: PackageManager,
|
|
pkg: string,
|
|
dev: boolean
|
|
): string => {
|
|
switch (manager) {
|
|
case "pnpm":
|
|
return `pnpm add ${dev ? "-D " : ""}${pkg}`;
|
|
case "npm":
|
|
return `npm install ${dev ? "--save-dev " : ""}${pkg}`;
|
|
case "yarn":
|
|
return `yarn add ${dev ? "-D " : ""}${pkg}`;
|
|
case "bun":
|
|
return `bun add ${dev ? "-d " : ""}${pkg}`;
|
|
default:
|
|
return pkg;
|
|
}
|
|
};
|
|
|
|
interface SnippetProps {
|
|
command: string;
|
|
}
|
|
|
|
const Snippet = ({ command }: SnippetProps) => {
|
|
const [isCopied, setIsCopied] = useState(false);
|
|
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
|
|
const copyToClipboard = useCallback(async () => {
|
|
if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
|
|
toast.error("Clipboard API not available");
|
|
return;
|
|
}
|
|
try {
|
|
await navigator.clipboard.writeText(command);
|
|
setIsCopied(true);
|
|
setTimeout(() => setIsCopied(false), 2000);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
toast.error(message);
|
|
}
|
|
}, [command]);
|
|
|
|
return (
|
|
<div className="flex items-center gap-3 bg-neutral-950 px-4 py-3 font-mono text-[13px] text-neutral-50">
|
|
<span aria-hidden className="select-none text-neutral-500">
|
|
$
|
|
</span>
|
|
<code className="flex-1 truncate bg-transparent p-0 text-neutral-50">
|
|
{command}
|
|
</code>
|
|
<Button
|
|
aria-label="Copy command"
|
|
className="size-7 shrink-0 text-neutral-300 hover:bg-neutral-800 hover:text-neutral-50"
|
|
onClick={copyToClipboard}
|
|
size="icon"
|
|
variant="ghost"
|
|
>
|
|
<Icon size={14} />
|
|
</Button>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export interface PackageInstallProps {
|
|
package: string;
|
|
dev?: boolean;
|
|
}
|
|
|
|
export const PackageInstall = ({
|
|
package: pkg,
|
|
dev = false,
|
|
}: PackageInstallProps) => (
|
|
<Tabs
|
|
className="not-prose mb-6 overflow-hidden rounded-md border bg-card"
|
|
defaultValue="pnpm"
|
|
>
|
|
<TabsList className="flex w-full gap-1 border-b bg-card px-2">
|
|
{PACKAGE_MANAGERS.map((manager) => (
|
|
<TabsTrigger
|
|
className={cn(
|
|
"group relative px-3 py-2 text-[15px] text-muted-foreground transition-colors",
|
|
"hover:text-foreground data-[state=active]:text-foreground"
|
|
)}
|
|
key={manager}
|
|
value={manager}
|
|
>
|
|
{manager}
|
|
<span className="absolute inset-x-0 -bottom-px h-px bg-transparent group-data-[state=active]:bg-foreground" />
|
|
</TabsTrigger>
|
|
))}
|
|
</TabsList>
|
|
{PACKAGE_MANAGERS.map((manager) => (
|
|
<TabsContent key={manager} value={manager}>
|
|
<Snippet command={buildCommand(manager, pkg, dev)} />
|
|
</TabsContent>
|
|
))}
|
|
</Tabs>
|
|
);
|