Files
copilotkit__copilotkit/docs/components/react/integrations.tsx
2026-03-02 11:30:16 -05:00

244 lines
8.0 KiB
TypeScript

import { IntegrationsSelectorLightDesktop } from "./integrations-index-selector/integrations-selector-light-desktop";
import { IntegrationsSelectorDarkDesktop } from "./integrations-index-selector/integrations-selector-dark-desktop";
import {
KiteIconLight,
KiteIconDark,
} from "./integrations-index-selector/kite-icon";
import { IntegrationsSelectorLightMobile } from "./integrations-index-selector/integrations-selector-light-mobile";
import { IntegrationsSelectorDarkMobile } from "./integrations-index-selector/integrations-selector-dark-mobile";
import { IntegrationLinkRoundedButton } from "./integration-link-button/integration-link-rounded-button";
import { ComponentType } from "react";
import {
INTEGRATION_ORDER,
IntegrationId,
getIntegration,
} from "@/lib/integrations";
import { hasIntegrationFeature } from "@/lib/integration-features";
import { AgentSpecMarkIcon, A2AIcon } from "@/lib/icons/custom-icons";
import AdkIcon from "../ui/icons/adk";
import Ag2Icon from "../ui/icons/ag2";
import CrewaiIcon from "../ui/icons/crewai";
import LanggraphIcon from "../ui/icons/langgraph";
import LlamaIndexIcon from "../ui/icons/llama-index";
import MastraIcon from "../ui/icons/mastra";
import AgnoIcon from "../ui/icons/agno";
import PydanticAiIcon from "../ui/icons/pydantic-ai";
import { MicrosoftIcon } from "../ui/icons/microsoft";
import { AwsStrandsIcon } from "../ui/icons/aws-strands";
// Icon mapping - component-specific
const INTEGRATION_ICONS: Record<
IntegrationId,
ComponentType<{ className?: string }>
> = {
"built-in-agent": () => <p>🪁</p>,
langgraph: LanggraphIcon,
adk: AdkIcon,
"microsoft-agent-framework": MicrosoftIcon,
"aws-strands": AwsStrandsIcon,
mastra: MastraIcon,
"pydantic-ai": PydanticAiIcon,
"crewai-flows": CrewaiIcon,
agno: AgnoIcon,
ag2: Ag2Icon,
"agent-spec": AgentSpecMarkIcon,
llamaindex: LlamaIndexIcon,
a2a: A2AIcon,
};
interface Integration {
id: IntegrationId;
label: string;
description: string;
Icon: ComponentType<{ className?: string }>;
href: string;
}
// Build integrations list from canonical order
const INTEGRATIONS: Integration[] = INTEGRATION_ORDER.map((id) => {
const meta = getIntegration(id);
return {
id,
label: meta.label,
description: meta.description,
Icon: INTEGRATION_ICONS[id],
href: meta.href,
};
});
interface IntegrationsGridProps {
targetPage?: string;
route?: string;
suppressDirectToLLM?: boolean;
variant?: "default" | "simple";
}
const IntegrationsGrid: React.FC<IntegrationsGridProps> = ({
targetPage,
route,
suppressDirectToLLM = false,
variant = "default",
}) => {
const hasTargetPage = (
integration: Integration,
targetPage: string,
): boolean => {
if (!targetPage) {
return true;
}
// Use auto-generated feature mapping
return hasIntegrationFeature(integration.id, targetPage);
};
const getHref = (integration: Integration) => {
if (targetPage) {
// Special case: direct-to-llm has pages in /guides/ subdirectory
if (integration.id === "built-in-agent") {
return `${integration.href}/guides/${targetPage}`;
}
return `${integration.href}/${targetPage}`;
}
if (route) {
return `${integration.href}/${route}`;
}
return integration.href;
};
let filteredIntegrations = INTEGRATIONS;
// Filter out Direct to LLM if suppressed
if (suppressDirectToLLM) {
filteredIntegrations = filteredIntegrations.filter(
(integration) => integration.id !== "built-in-agent",
);
}
// Filter out integrations that don't have the target page
if (targetPage) {
filteredIntegrations = filteredIntegrations.filter((integration) =>
hasTargetPage(integration, targetPage),
);
}
if (variant === "simple") {
return (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-8 my-8 not-prose">
{filteredIntegrations.map((integration) => (
<a
key={integration.id}
href={getHref(integration)}
className="group flex items-start gap-3 no-underline"
>
<div className="flex-shrink-0 mt-1">
<integration.Icon className="h-6 w-6 text-primary" />
</div>
<div>
<div className="font-semibold text-foreground group-hover:text-primary transition-colors">
{integration.label}{" "}
<span className="text-muted-foreground group-hover:text-primary">
&rsaquo;
</span>
</div>
<div className="text-sm text-muted-foreground leading-relaxed mt-0.5">
{integration.description}
</div>
</div>
</a>
))}
</div>
);
}
return (
<div className="flex flex-row flex-wrap justify-center items-center gap-x-6 gap-y-6 my-8">
{/* Large desktop: 4 columns (2xl+) */}
<div className="hidden 2xl:flex items-center">
{/* Kite icon - positioned separately to avoid SVG distortion */}
<div className="relative flex items-center">
<KiteIconLight className="block dark:hidden w-[120px] h-[120px]" />
<KiteIconDark className="hidden dark:block w-[120px] h-[120px]" />
</div>
{/* Connectors SVG - overlaps with kite to attach to circle edge */}
<div className="-ml-[40px]">
<IntegrationsSelectorLightDesktop
className="block dark:hidden"
rows={Math.ceil(filteredIntegrations.length / 4)}
rowHeight={60}
/>
<IntegrationsSelectorDarkDesktop
className="hidden dark:block"
rows={Math.ceil(filteredIntegrations.length / 4)}
rowHeight={60}
/>
</div>
<div className="grid grid-cols-4 gap-2">
{filteredIntegrations.map((integration) => (
<IntegrationLinkRoundedButton
key={integration.id}
label={integration.label}
Icon={integration.Icon}
href={getHref(integration)}
/>
))}
</div>
</div>
{/* Small screens (below lg): 2 columns with 36px row height */}
<div className="flex flex-row items-start gap-2 lg:hidden">
<div className="-ml-11 shrink-0">
<IntegrationsSelectorLightMobile
className="block dark:hidden"
rows={Math.ceil(filteredIntegrations.length / 2)}
rowHeight={36}
/>
<IntegrationsSelectorDarkMobile
className="hidden dark:block"
rows={Math.ceil(filteredIntegrations.length / 2)}
rowHeight={36}
/>
</div>
<div className="grid grid-cols-2 gap-2 -ml-5 pt-[90px]">
{filteredIntegrations.map((integration) => (
<IntegrationLinkRoundedButton
key={integration.id}
label={integration.label}
Icon={integration.Icon}
href={getHref(integration)}
/>
))}
</div>
</div>
{/* Medium screens (lg to 2xl): 2 columns with 60px row height */}
<div className="hidden lg:flex 2xl:hidden flex-row items-start gap-2">
<div className="-ml-11 shrink-0">
<IntegrationsSelectorLightMobile
className="block dark:hidden"
rows={Math.ceil(filteredIntegrations.length / 2)}
rowHeight={60}
/>
<IntegrationsSelectorDarkMobile
className="hidden dark:block"
rows={Math.ceil(filteredIntegrations.length / 2)}
rowHeight={60}
/>
</div>
<div className="grid grid-cols-2 gap-2 -ml-5 pt-[90px]">
{filteredIntegrations.map((integration) => (
<IntegrationLinkRoundedButton
key={integration.id}
label={integration.label}
Icon={integration.Icon}
href={getHref(integration)}
/>
))}
</div>
</div>
</div>
);
};
export { IntegrationsGrid };
export type { IntegrationsGridProps };