/** * A2UI Catalog — React Renderers * * Each renderer maps a component name from definitions.ts to a React * implementation. Props are type-checked against the Zod schemas. * * To add a component: define its schema in definitions.ts, then add a * renderer here. See README.md "Adding a custom component" for details. * * The assembled catalog is registered in layout.tsx via * . */ "use client"; import React, { useState } from "react"; import { PieChart as RechartsPie, Pie, Cell, ResponsiveContainer, BarChart as RechartsBar, Bar, XAxis, YAxis, Tooltip, CartesianGrid, } from "recharts"; import { createCatalog } from "@copilotkit/a2ui-renderer"; import type { CatalogRenderers } from "@copilotkit/a2ui-renderer"; import { demonstrationCatalogDefinitions } from "./definitions"; import type { DemonstrationCatalogDefinitions } from "./definitions"; // ─── Theme-aware colors ───────────────────────────────────────────── const c = { card: "var(--card)", cardFg: "var(--card-foreground)", border: "var(--border)", muted: "var(--muted-foreground)", divider: "color-mix(in srgb, var(--border) 50%, var(--card))", shadow: "0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.04)", btnBg: "color-mix(in srgb, var(--muted) 40%, var(--card))", btnDoneBg: "color-mix(in srgb, #22c55e 10%, var(--card))", }; function ActionButton({ label, doneLabel, action, children: child, }: { label: string; doneLabel: string; action: any; children?: React.ReactNode; }) { const [done, setDone] = useState(false); return ( ); } // ─── Renderers (type-checked against schema definitions) ──────────── const demonstrationCatalogRenderers: CatalogRenderers = { Title: ({ props }) => { const Tag = ( props.level === "h1" ? "h1" : props.level === "h3" ? "h3" : "h2" ) as keyof JSX.IntrinsicElements; const sizes: Record = { h1: "1.75rem", h2: "1.25rem", h3: "1rem", }; return ( {props.text} ); }, // Text: removed — use the basic catalog's Text (supports DynamicStringSchema // for path bindings in fixed-schema templates). Row: ({ props, children }) => { const justifyMap: Record = { start: "flex-start", center: "center", end: "flex-end", spaceBetween: "space-between", }; const items = Array.isArray(props.children) ? props.children : []; return (
{items.map((item: any, i: number) => { if (typeof item === "string") return (
{children(item)}
); if (item && typeof item === "object" && "id" in item) return (
{(children as any)(item.id, item.basePath)}
); return null; })}
); }, Column: ({ props, children }) => { const items = Array.isArray(props.children) ? props.children : []; return (
{items.map((item: any, i: number) => { if (typeof item === "string") return ( {children(item)} ); if (item && typeof item === "object" && "id" in item) return ( {(children as any)(item.id, item.basePath)} ); return null; })}
); }, DashboardCard: ({ props, children }) => (
{props.title}
{props.subtitle && (
{props.subtitle}
)}
{props.child && children(props.child)}
), Metric: ({ props }) => { const trendColors: Record = { up: "#059669", down: "#dc2626", neutral: c.muted, }; const trendIcons: Record = { up: "↑", down: "↓", neutral: "→", }; return (
{props.label}
{props.value} {props.trend && props.trendValue && ( {trendIcons[props.trend]} {props.trendValue} )}
); }, PieChart: ({ props }) => { const COLORS = [ "#3b82f6", "#8b5cf6", "#ec4899", "#f59e0b", "#10b981", "#6366f1", ]; const data = props.data ?? []; return (
{data.map((entry: any, i: number) => ( ))}
); }, BarChart: ({ props }) => { const data = props.data ?? []; return (
); }, Badge: ({ props }) => { const variants: Record = { success: { bg: "#dcfce7", color: "#166534" }, warning: { bg: "#fef3c7", color: "#92400e" }, error: { bg: "#fee2e2", color: "#991b1b" }, info: { bg: "#dbeafe", color: "#1e40af" }, neutral: { bg: "var(--muted)", color: c.cardFg }, }; const v = variants[props.variant ?? "neutral"] ?? variants.neutral; return ( {props.text} ); }, DataTable: ({ props }) => { const cols = props.columns ?? []; const rows = props.rows ?? []; return (
{cols.map((col: any) => ( ))} {rows.map((row: any, i: number) => ( {cols.map((col: any) => ( ))} ))}
{col.label}
{String(row[col.key] ?? "")}
); }, Button: ({ props, children }) => { return ( {props.child ? children(props.child) : null} ); }, FlightCard: ({ props: rawProps }) => { // The binder resolves path bindings to strings at runtime. const props = rawProps as Record; const statusColors: Record = { "On Time": "#22c55e", Delayed: "#eab308", Cancelled: "#ef4444", }; const dotColor = props.statusColor ?? statusColors[props.status] ?? "#22c55e"; return (
{/* Header: airline + price */}
{props.airline} {props.airline}
{props.price}
{/* Meta */}
{props.flightNumber} {props.date}

{/* Times */}
{props.departureTime} {props.duration} {props.arrivalTime}
{/* Route */}
{props.origin} {props.destination}

{/* Status */}
{props.status}
); }, }; // ─── Assembled Catalog ─────────────────────────────────────────────── export const demonstrationCatalog = createCatalog( demonstrationCatalogDefinitions, demonstrationCatalogRenderers, { catalogId: "copilotkit://app-dashboard-catalog", }, );