mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
8a872529fe
* docs: make /worlds the canonical home for World docs The world pages (Local/Postgres/Vercel) and Building a World were duplicated inside the v4 and v5 docs trees while /worlds/[id] rendered the v4 copy — hiding v5-only content like multi-region and leaving two diverging sources of truth. - Move world docs to an unversioned docs/content/worlds/ collection (based on the v5 copies, with inline 4.x callouts for factory naming and 5.x-only env vars), rendered at /worlds/* - Add /worlds/building-a-world; flatten the docs Deploying section to a single intro page and drop its Rocket icon - Point every link, frontmatter ref, and worlds-manifest docs field at /worlds/*; add redirects for the removed v5 and building-a-world URLs - Keep world docs on agent-facing surfaces: search, llms.txt, sitemap.md/.xml, and .md exports now serve the worlds collection - Extend the docs link linter to validate worlds pages (with heading anchors) and their outgoing links Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * docs: version the world docs like the docs trees (v4/v5 switcher) Instead of a single unversioned copy, world docs now follow the same versioning strategy as the docs pages: content/worlds/v4 is served at /worlds/* (current) and content/worlds/v5 at /v5/worlds/*, restoring the original per-version content. Each world detail page (and Building a World) renders the docs version switcher — the worlds listing page has no natural home for it, so it lives on the world pages themselves. - Render-time href rewriting on v5 pages now covers /worlds/... links (shared rewriteHrefForVersion helper, also used by the v5 docs and cookbook routes), and the markdown-export rewrite does the same - v5 world pages are noindexed with a canonical to /worlds/<id>; community worlds stay unversioned (/v5/worlds/<id> redirects) - /v5/docs/deploying/world/* redirects now land on /v5/worlds/*; /v5/worlds and /v5/worlds/compare redirect to the unversioned pages - Link linter models the versioned worlds URL spaces (v5 pages resolve /worlds hrefs against the v5 collection); sitemap.md and the .md export routes cover /v5/worlds/* Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> * docs: fix v4 multi-region anchor and tighten version-prefix matching Address PR review: - The v4 Deploying page linked /worlds/vercel#multi-region, but the Multi-region section only exists on the v5 world page; use the explicit cross-version /v5/worlds/vercel#multi-region link (this was the Docs Links CI failure) - rewriteHrefForVersion now uses the boundary-checked hasPathPrefix (shared leaf module lib/geistdocs/path-prefix.ts, also used by source.ts) instead of bare startsWith - buildVersionUrl's shared-route fast path is segment-based rather than substring includes() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> --------- Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
237 lines
7.2 KiB
TypeScript
237 lines
7.2 KiB
TypeScript
'use client';
|
|
|
|
import { Button } from '@vercel/geistdocs/components/button';
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from '@vercel/geistdocs/components/tooltip';
|
|
import {
|
|
BadgeCheck,
|
|
CheckIcon,
|
|
ChevronRight,
|
|
Code,
|
|
CopyIcon,
|
|
ExternalLink,
|
|
Github,
|
|
HeartHandshake,
|
|
Package,
|
|
ShieldCheck,
|
|
} from 'lucide-react';
|
|
import Link from 'next/link';
|
|
import type { ReactNode } from 'react';
|
|
import { useState } from 'react';
|
|
import { toast } from 'sonner';
|
|
import {
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
BreadcrumbLink,
|
|
BreadcrumbList,
|
|
BreadcrumbPage,
|
|
BreadcrumbSeparator,
|
|
} from '@/components/ui/breadcrumb';
|
|
|
|
import type { World } from './types';
|
|
|
|
interface WorldDetailHeroProps {
|
|
id: string;
|
|
world: World;
|
|
/** Docs version switcher slot, rendered above the quick links. */
|
|
versionSelect?: ReactNode;
|
|
}
|
|
|
|
export function WorldDetailHero({
|
|
id,
|
|
world,
|
|
versionSelect,
|
|
}: WorldDetailHeroProps) {
|
|
const [copied, setCopied] = useState(false);
|
|
|
|
const installCommand = `npm i ${world.package}`;
|
|
|
|
const handleCopy = () => {
|
|
try {
|
|
navigator.clipboard.writeText(installCommand);
|
|
setCopied(true);
|
|
setTimeout(() => {
|
|
setCopied(false);
|
|
}, 2000);
|
|
} catch (error) {
|
|
const message =
|
|
error instanceof Error
|
|
? error.message
|
|
: 'Failed to copy text to clipboard';
|
|
toast.error(message);
|
|
}
|
|
};
|
|
|
|
const CopyButtonIcon = copied ? CheckIcon : CopyIcon;
|
|
|
|
// GitHub source URL for official worlds
|
|
const githubUrl =
|
|
world.repository ||
|
|
(world.type === 'official'
|
|
? `https://github.com/vercel/workflow/tree/main/packages/world-${id}`
|
|
: null);
|
|
|
|
return (
|
|
<section className="space-y-6 pt-8 sm:pt-12 pb-12 border-b">
|
|
{/* Breadcrumbs */}
|
|
<Breadcrumb>
|
|
<BreadcrumbList>
|
|
<BreadcrumbItem>
|
|
<BreadcrumbLink asChild>
|
|
<Link href="/worlds">Worlds</Link>
|
|
</BreadcrumbLink>
|
|
</BreadcrumbItem>
|
|
<BreadcrumbSeparator>
|
|
<ChevronRight className="h-4 w-4" />
|
|
</BreadcrumbSeparator>
|
|
<BreadcrumbItem>
|
|
<BreadcrumbPage>{world.name}</BreadcrumbPage>
|
|
</BreadcrumbItem>
|
|
</BreadcrumbList>
|
|
</Breadcrumb>
|
|
|
|
{/* Main content grid */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-[1fr_200px] gap-8 lg:gap-12">
|
|
{/* Left side - Title and description */}
|
|
<div className="space-y-4 min-w-0">
|
|
<h1 className="text-4xl font-semibold tracking-tight sm:text-5xl flex items-center gap-4">
|
|
{world.name}
|
|
{world.type === 'official' ? (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<BadgeCheck className="h-8 w-8 text-blue-900" />
|
|
</TooltipTrigger>
|
|
<TooltipContent side="top">
|
|
<p className="text-xs">Maintained by Vercel</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
) : (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<HeartHandshake className="h-8 w-8 text-pink-900" />
|
|
</TooltipTrigger>
|
|
<TooltipContent side="top">
|
|
<p className="text-xs">Maintained by the community</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
</h1>
|
|
<p className="font-mono text-sm text-muted-foreground">
|
|
{world.package}
|
|
</p>
|
|
<p className="text-lg text-muted-foreground max-w-2xl">
|
|
{world.description}
|
|
</p>
|
|
|
|
{/* Actions - Community worlds only */}
|
|
{world.type === 'community' && (
|
|
<div className="flex items-center gap-3 flex-wrap pt-2">
|
|
<div className="relative bg-background border rounded-md overflow-hidden py-3 pl-4 pr-12 inline-flex">
|
|
<pre className="text-sm">
|
|
<code>{installCommand}</code>
|
|
</pre>
|
|
<Button
|
|
onClick={handleCopy}
|
|
size="icon"
|
|
variant="ghost"
|
|
className="absolute right-1 top-1/2 -translate-y-1/2"
|
|
>
|
|
<CopyButtonIcon className="size-4 text-muted-foreground" />
|
|
</Button>
|
|
</div>
|
|
{world.repository && (
|
|
<Button
|
|
asChild
|
|
variant="outline"
|
|
size="lg"
|
|
className="h-[44px] text-base"
|
|
>
|
|
<a
|
|
href={world.repository}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center gap-2"
|
|
>
|
|
GitHub
|
|
<ExternalLink className="h-4 w-4" />
|
|
</a>
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Right side - Quick links */}
|
|
<div className="space-y-2 text-sm">
|
|
{/* Docs version switcher */}
|
|
{versionSelect ? <div className="mb-4">{versionSelect}</div> : null}
|
|
|
|
{/* NPM Package */}
|
|
<a
|
|
href={`https://www.npmjs.com/package/${world.package}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
>
|
|
<Package className="h-4 w-4 shrink-0" />
|
|
<span>npm</span>
|
|
</a>
|
|
|
|
{/* GitHub */}
|
|
{githubUrl && (
|
|
<a
|
|
href={githubUrl}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
>
|
|
<Github className="h-4 w-4 shrink-0" />
|
|
<span>Source</span>
|
|
</a>
|
|
)}
|
|
|
|
{/* Example */}
|
|
{world.example && (
|
|
<a
|
|
href={world.example}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
>
|
|
<Code className="h-4 w-4 shrink-0" />
|
|
<span>Example</span>
|
|
</a>
|
|
)}
|
|
|
|
{/* Encryption */}
|
|
{world.features.includes('encryption') && (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Link
|
|
href="/docs/how-it-works/encryption"
|
|
className="flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
|
|
>
|
|
<ShieldCheck className="h-4 w-4 shrink-0 text-blue-900" />
|
|
<span>E2E Encrypted</span>
|
|
</Link>
|
|
</TooltipTrigger>
|
|
<TooltipContent
|
|
side="top"
|
|
align="start"
|
|
className="max-w-[200px]"
|
|
>
|
|
<p className="text-xs">
|
|
User data is encrypted end-to-end in the event log
|
|
</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|