/** * 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. */ import React, { useState } from "react"; import { PieChart as RechartsPie, Pie, Cell, ResponsiveContainer, BarChart as RechartsBar, Bar, XAxis, YAxis, Tooltip, CartesianGrid, } from "recharts"; import { createCatalog, type CatalogRenderers, } from "@copilotkit/a2ui-renderer"; import { demonstrationCatalogDefinitions, 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: unknown; 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 React.JSX.IntrinsicElements; const sizes: Record = { h1: "1.75rem", h2: "1.25rem", h3: "1rem", }; return ( {props.text} ); }, 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: unknown, i: number) => { if (typeof item === "string") return (
{children(item)}
); if ( item && typeof item === "object" && "id" in (item as Record) ) return (
).id}-${i}`} style={{ flex: "1 1 0", minWidth: 0 }} > {( children as ( id: string, basePath?: string, ) => React.ReactNode )( (item as Record).id, (item as Record).basePath, )}
); return null; })}
); }, Column: ({ props, children }) => { const items = Array.isArray(props.children) ? props.children : []; return (
{items.map((item: unknown, i: number) => { if (typeof item === "string") return ( {children(item)} ); if ( item && typeof item === "object" && "id" in (item as Record) ) return ( ).id}-${i}`} > {( children as ( id: string, basePath?: string, ) => React.ReactNode )( (item as Record).id, (item as Record).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: "\u2191", down: "\u2193", neutral: "\u2192", }; 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: Record, 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: Record) => ( ))} {rows.map((row: Record, i: number) => ( {cols.map((col: Record) => ( ))} ))}
{col.label as string}
{String(row[col.key as string] ?? "")}
); }, 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 as string) ?? statusColors[props.status as string] ?? "#22c55e"; return (
{/* Header: airline + price */}
{props.airline {props.airline as string}
{props.price as string}
{/* Meta */}
{props.flightNumber as string} {props.date as string}

{/* Times */}
{props.departureTime as string} {props.duration as string} {props.arrivalTime as string}
{/* Route */}
{props.origin as string} {"\u2192"} {props.destination as string}

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