mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
4d7965b802
Adds an optional `afterFeatures` ReactNode prop to the FrameworkOverview landing-page component, rendered as a section between the supported features grid and the architecture section. Lets the hosting MDX page inject contextual content (links, callouts, sign-up cards) below the final feature without needing to modify the component for each integration.
468 lines
18 KiB
TypeScript
468 lines
18 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
ExternalLink,
|
|
ArrowRight,
|
|
Copy,
|
|
Check,
|
|
PlayIcon,
|
|
BookOpen,
|
|
LayoutIcon,
|
|
} from "lucide-react";
|
|
import Link from "next/link";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useState, ReactNode } from "react";
|
|
import { ImageZoom } from "fumadocs-ui/components/image-zoom";
|
|
import Image from "next/image";
|
|
|
|
export interface FrameworkFeature {
|
|
title: string;
|
|
description: string;
|
|
documentationLink: string;
|
|
demoLink: string;
|
|
videoUrl?: string;
|
|
imageUrl?: string;
|
|
}
|
|
|
|
export interface LiveDemo {
|
|
type: "saas" | "canvas";
|
|
title: string;
|
|
description: string;
|
|
iframeUrl: string;
|
|
}
|
|
|
|
export interface FrameworkOverviewProps {
|
|
frameworkName: string;
|
|
frameworkIcon: ReactNode;
|
|
header: string;
|
|
subheader: string;
|
|
displayBannerSubheader?: boolean;
|
|
bannerVideo?: string;
|
|
bannerImage?: string;
|
|
guideLink: string;
|
|
initCommand: string;
|
|
featuresLink: string;
|
|
supportedFeatures?: FrameworkFeature[];
|
|
/** Optional content rendered immediately after the supported features section. */
|
|
afterFeatures?: ReactNode;
|
|
architectureImage?: string;
|
|
architectureVideo?: string;
|
|
liveDemos: LiveDemo[];
|
|
tutorialLink?: string;
|
|
}
|
|
|
|
export function FrameworkOverview({
|
|
frameworkName,
|
|
frameworkIcon,
|
|
header,
|
|
subheader,
|
|
displayBannerSubheader = true,
|
|
bannerVideo,
|
|
bannerImage,
|
|
guideLink,
|
|
initCommand,
|
|
featuresLink,
|
|
supportedFeatures = [],
|
|
afterFeatures,
|
|
architectureImage,
|
|
architectureVideo,
|
|
liveDemos,
|
|
tutorialLink,
|
|
}: FrameworkOverviewProps) {
|
|
const [activeDemo, setActiveDemo] = useState<"saas" | "canvas">(
|
|
liveDemos[0]?.type || "saas",
|
|
);
|
|
const [copied, setCopied] = useState(false);
|
|
const currentDemo = liveDemos.find((demo) => demo.type === activeDemo);
|
|
|
|
const handleCopyCommand = () => {
|
|
navigator.clipboard.writeText(initCommand);
|
|
setCopied(true);
|
|
setTimeout(() => setCopied(false), 2000);
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen">
|
|
<div className="mx-auto max-w-5xl px-4 sm:px-6">
|
|
{/* Header with Fixed Buttons */}
|
|
<header className="text-center mb-12 sm:mb-20">
|
|
<div className="hidden items-center justify-center gap-4 sm:gap-8 mb-8 sm:mb-12 flex-wrap px-4 lg:flex">
|
|
<div className="flex items-center gap-2 sm:gap-4">
|
|
<Image
|
|
src="https://cdn.copilotkit.ai/docs/copilotkit/icons/copilotkit-color.svg"
|
|
alt="CopilotKit"
|
|
height={40}
|
|
width={40}
|
|
className="w-8 h-8 sm:w-10 sm:h-10 flex-shrink-0"
|
|
/>
|
|
<span className="text-2xl sm:text-3xl font-bold whitespace-nowrap">
|
|
CopilotKit
|
|
</span>
|
|
</div>
|
|
<div className="w-px h-10 sm:h-12 bg-border dark:bg-primary flex-shrink-0" />
|
|
<div className="flex items-center gap-2 sm:gap-4">
|
|
<div className="flex items-center justify-center text-primary">
|
|
{frameworkIcon}
|
|
</div>
|
|
<span className="text-2xl sm:text-3xl font-bold text-foreground whitespace-nowrap">
|
|
{frameworkName}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<h1 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6 text-foreground tracking-wider leading-tight text-balance px-4">
|
|
{header}
|
|
</h1>
|
|
<p className="text-base sm:text-lg md:text-xl text-muted-foreground mb-8 sm:mb-12 max-w-3xl mx-auto leading-relaxed text-pretty px-4">
|
|
{subheader}
|
|
</p>
|
|
|
|
<div className="flex flex-wrap lg:flex-nowrap justify-center gap-3 sm:gap-4 px-4">
|
|
{/* Quickstart and View Features stay together on small screens */}
|
|
<div className="flex gap-3 sm:gap-4 w-full lg:w-auto lg:contents">
|
|
<Link
|
|
href={guideLink}
|
|
className="flex-1 lg:flex-none lg:w-auto lg:order-1"
|
|
>
|
|
<Button
|
|
size="lg"
|
|
variant="outline"
|
|
className="w-full px-6 sm:px-8 py-3 text-sm sm:text-base cursor-pointer"
|
|
>
|
|
Quickstart
|
|
</Button>
|
|
</Link>
|
|
<Link
|
|
href={featuresLink}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="flex-1 lg:flex-none lg:w-auto lg:order-3"
|
|
>
|
|
<Button
|
|
size="lg"
|
|
variant="outline"
|
|
className="w-full px-6 sm:px-8 py-3 text-sm sm:text-base cursor-pointer"
|
|
>
|
|
View Features
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
<Button
|
|
size="lg"
|
|
className="w-full lg:w-auto bg-primary/10 dark:bg-primary/20 text-primary hover:bg-primary/20 dark:hover:bg-primary/40 shadow-lg px-6 sm:px-8 py-3 text-sm sm:text-base font-mono cursor-pointer border border-primary lg:order-2"
|
|
onClick={handleCopyCommand}
|
|
>
|
|
<span className="truncate">npx copilotkit create</span>
|
|
{copied ? (
|
|
<Check className="ml-2 h-4 w-4 flex-shrink-0" />
|
|
) : (
|
|
<Copy className="ml-2 h-4 w-4 flex-shrink-0" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</header>
|
|
|
|
{/* Overview Video */}
|
|
<section className="mb-12 sm:mb-24">
|
|
<div>
|
|
{bannerVideo && (
|
|
<video
|
|
src={bannerVideo}
|
|
className="w-full rounded-lg sm:rounded-xl border shadow-lg"
|
|
controls
|
|
autoPlay
|
|
muted
|
|
loop
|
|
playsInline
|
|
/>
|
|
)}
|
|
{bannerImage && (
|
|
<ImageZoom
|
|
src={bannerImage}
|
|
alt="CopilotKit Banner"
|
|
height={100}
|
|
width={50}
|
|
className="w-full rounded-lg sm:rounded-xl border shadow-lg"
|
|
/>
|
|
)}
|
|
</div>
|
|
{displayBannerSubheader && (
|
|
<p className="text-xs sm:text-sm text-muted-foreground mt-4 text-center px-4">
|
|
Starter app from running{" "}
|
|
<span className="font-mono bg-primary/10 text-primary px-2 py-1 rounded-md text-xs sm:text-sm">
|
|
{initCommand}
|
|
</span>
|
|
, demonstrating key features of CopilotKit with {frameworkName}.
|
|
</p>
|
|
)}
|
|
</section>
|
|
|
|
{/* Features - Only show if features are provided */}
|
|
{supportedFeatures.length > 0 && (
|
|
<section className="mb-12 sm:mb-24">
|
|
<div className="mb-8 sm:mb-16 text-center px-4">
|
|
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-foreground">
|
|
Key Features
|
|
</h2>
|
|
<p className="text-base sm:text-lg text-muted-foreground max-w-3xl mx-auto">
|
|
Everything you need to build interactive, agent-powered
|
|
applications
|
|
</p>
|
|
<div className="w-16 sm:w-24 h-1 bg-gradient-to-r from-primary to-primary mx-auto mt-4 sm:mt-6 rounded-full"></div>
|
|
</div>
|
|
|
|
<div className="space-y-12 sm:space-y-24">
|
|
{supportedFeatures.map((feature, index) => (
|
|
<div
|
|
key={feature.title}
|
|
className={`border-b border-border pb-12 sm:pb-24 ${index === supportedFeatures.length - 1 ? "last:border-b-0 last:pb-0" : ""}`}
|
|
>
|
|
<div className="grid lg:grid-cols-5 gap-6 sm:gap-12 items-start">
|
|
<div className="lg:col-span-2">
|
|
<div className="mb-4">
|
|
<h3 className="text-xl sm:text-2xl font-bold mb-2 text-foreground">
|
|
{feature.title}
|
|
</h3>
|
|
<div className="w-10 sm:w-12 h-0.5 bg-gradient-to-r from-primary to-primary rounded-full"></div>
|
|
</div>
|
|
<p className="text-sm sm:text-base text-muted-foreground leading-relaxed mb-6">
|
|
{feature.description}
|
|
</p>
|
|
<div className="space-y-3">
|
|
<Link
|
|
href={feature.documentationLink}
|
|
className="block text-primary hover:text-primary font-medium no-underline text-sm sm:text-base"
|
|
>
|
|
Learn more →
|
|
</Link>
|
|
<Link
|
|
href={feature.demoLink}
|
|
className="block text-muted-foreground hover:text-foreground text-xs sm:text-sm flex items-center gap-2 no-underline"
|
|
>
|
|
<ExternalLink className="h-4 w-4" />
|
|
Live demo
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="lg:col-span-3">
|
|
{feature.videoUrl && (
|
|
<video
|
|
src={feature.videoUrl}
|
|
className="w-full rounded-lg border shadow-lg"
|
|
controls
|
|
autoPlay
|
|
muted
|
|
loop
|
|
playsInline
|
|
/>
|
|
)}
|
|
{feature.imageUrl && (
|
|
<ImageZoom
|
|
src={feature.imageUrl}
|
|
alt="CopilotKit Feature Image"
|
|
height={100}
|
|
width={50}
|
|
className="w-full rounded-lg border shadow-lg"
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{afterFeatures && (
|
|
<section className="mb-12 sm:mb-24 px-4">{afterFeatures}</section>
|
|
)}
|
|
|
|
{/* Architecture */}
|
|
<section className="mb-12 sm:mb-24">
|
|
<div className="mb-8 sm:mb-12 text-center px-4">
|
|
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-foreground">
|
|
Architecture
|
|
</h2>
|
|
<p className="text-base sm:text-lg text-muted-foreground max-w-3xl mx-auto">
|
|
Understanding how CopilotKit and {frameworkName} work together
|
|
</p>
|
|
<div className="w-16 sm:w-24 h-1 bg-gradient-to-r from-primary to-primary mx-auto mt-4 sm:mt-6 rounded-full"></div>
|
|
</div>
|
|
{architectureImage && (
|
|
<ImageZoom
|
|
src={architectureImage}
|
|
alt={`CopilotKit ${frameworkName} Infrastructure Diagram`}
|
|
height={100}
|
|
width={50}
|
|
className="w-full rounded-lg sm:rounded-xl shadow-lg border"
|
|
/>
|
|
)}
|
|
{architectureVideo && (
|
|
<div className="relative">
|
|
<video
|
|
src={architectureVideo}
|
|
className="w-full rounded-lg sm:rounded-xl border shadow-lg"
|
|
controls
|
|
autoPlay
|
|
muted
|
|
loop
|
|
playsInline
|
|
/>
|
|
</div>
|
|
)}
|
|
</section>
|
|
|
|
{/* Live demo - Only show if demos are provided */}
|
|
{liveDemos.length > 0 && (
|
|
<section className="mb-12 sm:mb-24">
|
|
<div className="mb-8 sm:mb-12 text-center px-4">
|
|
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-foreground">
|
|
Live demo
|
|
</h2>
|
|
<p className="text-base sm:text-lg text-muted-foreground max-w-3xl mx-auto mb-6 sm:mb-8">
|
|
Explore different types of agent-powered applications built with
|
|
CopilotKit and {frameworkName}.
|
|
</p>
|
|
|
|
{/* Demo Toggle Buttons */}
|
|
{liveDemos.length > 1 && (
|
|
<div className="flex flex-col sm:flex-row justify-center gap-3 sm:gap-4 mb-6 sm:mb-8">
|
|
{liveDemos.map((demo) => (
|
|
<Button
|
|
key={demo.type}
|
|
onClick={() => setActiveDemo(demo.type)}
|
|
className={`px-4 sm:px-6 py-2 cursor-pointer text-sm sm:text-base ${
|
|
activeDemo === demo.type
|
|
? "bg-primary/10 text-primary hover:bg-primary/20 shadow border border-primary"
|
|
: "bg-secondary text-secondary-foreground hover:bg-secondary"
|
|
}`}
|
|
>
|
|
{demo.title}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
<div className="w-16 sm:w-24 h-1 bg-gradient-to-r from-primary to-primary mx-auto mt-4 sm:mt-6 rounded-full"></div>
|
|
</div>
|
|
|
|
<div className="max-w-4xl mx-auto mt-6 sm:mt-8 mb-8 sm:mb-16 px-4">
|
|
{liveDemos.find((demo) => demo.type === activeDemo) && (
|
|
<div className="text-center">
|
|
<h3 className="text-base sm:text-lg font-semibold text-foreground mb-2">
|
|
{liveDemos.find((demo) => demo.type === activeDemo)?.title}
|
|
</h3>
|
|
<p className="text-sm sm:text-base text-muted-foreground leading-relaxed">
|
|
{
|
|
liveDemos.find((demo) => demo.type === activeDemo)
|
|
?.description
|
|
}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="relative">
|
|
{liveDemos.find((demo) => demo.type === activeDemo) && (
|
|
<iframe
|
|
src={
|
|
liveDemos.find((demo) => demo.type === activeDemo)
|
|
?.iframeUrl
|
|
}
|
|
className="w-full h-[400px] sm:h-[600px] rounded-lg sm:rounded-xl border shadow-lg"
|
|
title={`${liveDemos.find((demo) => demo.type === activeDemo)?.title} Demo`}
|
|
/>
|
|
)}
|
|
<div className="absolute inset-0 rounded-lg sm:rounded-xl ring-1 ring-inset ring-secondary pointer-events-none"></div>
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{/* Standardized Next Steps */}
|
|
<section>
|
|
<div className="mb-8 sm:mb-12 text-center px-4">
|
|
<h2 className="text-3xl sm:text-4xl font-bold mb-4 text-foreground">
|
|
Next Steps
|
|
</h2>
|
|
<p className="text-base sm:text-lg text-muted-foreground max-w-3xl mx-auto">
|
|
Ready to build your own agent-powered application?
|
|
</p>
|
|
<div className="w-16 sm:w-24 h-1 bg-gradient-to-r from-primary to-primary mx-auto mt-4 sm:mt-6 rounded-full"></div>
|
|
</div>
|
|
<div
|
|
className={`grid gap-6 sm:gap-8 ${tutorialLink ? "grid-cols-1 xl:grid-cols-3" : "grid-cols-1 xl:grid-cols-2"}`}
|
|
>
|
|
<div className="border border-border rounded-lg p-6 sm:p-8 shadow bg-card flex flex-col justify-between">
|
|
<div className="flex flex-col">
|
|
<div className="flex items-center gap-2">
|
|
<PlayIcon className="text-primary w-5 h-5" />
|
|
<h3 className="text-lg sm:text-xl font-semibold !m-0 text-foreground">
|
|
Quickstart
|
|
</h3>
|
|
</div>
|
|
<p className="text-sm sm:text-base text-muted-foreground mb-6 sm:mb-8 leading-relaxed">
|
|
Build your first agentic app with {frameworkName} in minutes.
|
|
</p>
|
|
</div>
|
|
<Link href={guideLink} className="no-underline">
|
|
<Button className="w-full h-10 sm:h-11 text-sm sm:text-base bg-primary/10 text-primary hover:bg-primary/20 shadow border border-primary cursor-pointer">
|
|
Quickstart
|
|
<ArrowRight className="ml-2 h-4 w-4" />
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="border border-border rounded-lg p-6 sm:p-8 shadow bg-card flex flex-col justify-between">
|
|
<div className="flex flex-col">
|
|
<div className="flex items-center gap-2">
|
|
<LayoutIcon className="text-primary w-5 h-5" />
|
|
<h3 className="text-lg sm:text-xl font-semibold !m-0 text-foreground">
|
|
Feature Overview
|
|
</h3>
|
|
</div>
|
|
<p className="text-sm sm:text-base text-muted-foreground mb-6 sm:mb-8 leading-relaxed">
|
|
Try the key features provided to your agent by CopilotKit.
|
|
</p>
|
|
</div>
|
|
<Link
|
|
href={featuresLink}
|
|
rel="noopener noreferrer"
|
|
target="_blank"
|
|
className="no-underline"
|
|
>
|
|
<Button className="w-full h-10 sm:h-11 text-sm sm:text-base bg-primary/10 text-primary hover:bg-primary/20 shadow border border-primary cursor-pointer">
|
|
Visit feature viewer
|
|
<ArrowRight className="ml-2 h-4 w-4" />
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
|
|
{tutorialLink && (
|
|
<div className="border border-border rounded-lg p-6 sm:p-8 shadow bg-card flex flex-col justify-between">
|
|
<div className="flex flex-col">
|
|
<div className="flex items-center gap-2">
|
|
<BookOpen className="text-primary w-5 h-5" />
|
|
<h3 className="text-lg sm:text-xl font-semibold !m-0 text-foreground">
|
|
Tutorial
|
|
</h3>
|
|
</div>
|
|
<p className="text-sm sm:text-base text-muted-foreground mb-6 sm:mb-8 leading-relaxed">
|
|
Step-by-step guide to building an agent-native application.
|
|
</p>
|
|
</div>
|
|
<Link href={tutorialLink} className="no-underline">
|
|
<Button className="w-full h-10 sm:h-11 text-sm sm:text-base bg-primary/10 text-primary hover:bg-primary/20 shadow border border-primary cursor-pointer">
|
|
Start Tutorial
|
|
<ArrowRight className="ml-2 h-4 w-4" />
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|