UNPKG

@copilotkit/a2ui-renderer

Version:

A2UI Renderer for CopilotKit - render A2UI surfaces in React applications

1 lines 6.24 kB
{"version":3,"file":"A2uiSurface.mjs","names":[],"sources":["../../../src/react-renderer/a2ui-react/A2uiSurface.tsx"],"sourcesContent":["/**\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { useSyncExternalStore, memo, useMemo, useCallback } from \"react\";\nimport {\n type SurfaceModel,\n ComponentContext,\n type ComponentModel,\n} from \"@a2ui/web_core/v0_9\";\nimport type { ReactComponentImplementation } from \"./adapter\";\n\nconst ResolvedChild = memo(\n ({\n surface,\n id,\n basePath,\n compImpl,\n componentModel,\n }: {\n surface: SurfaceModel<ReactComponentImplementation>;\n id: string;\n basePath: string;\n componentModel: ComponentModel;\n compImpl: ReactComponentImplementation;\n }) => {\n const ComponentToRender = compImpl.render;\n\n // Create context. Recreate if the componentModel instance changes (e.g. type change recreation).\n const context = useMemo(\n () => new ComponentContext(surface, id, basePath),\n // componentModel is used as a trigger for recreation even if not in the body\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [surface, id, basePath, componentModel],\n );\n\n const buildChild = useCallback(\n (childId: string, specificPath?: string) => {\n const path = specificPath || context.dataContext.path;\n return (\n <DeferredChild\n key={`${childId}-${path}`}\n surface={surface}\n id={childId}\n basePath={path}\n />\n );\n },\n [surface, context.dataContext.path],\n );\n\n return <ComponentToRender context={context} buildChild={buildChild} />;\n },\n);\nResolvedChild.displayName = \"ResolvedChild\";\n\nexport const DeferredChild: React.FC<{\n surface: SurfaceModel<ReactComponentImplementation>;\n id: string;\n basePath: string;\n}> = memo(({ surface, id, basePath }) => {\n // 1. Subscribe specifically to this component's existence\n const store = useMemo(() => {\n let version = 0;\n return {\n subscribe: (cb: () => void) => {\n const unsub1 = surface.componentsModel.onCreated.subscribe((comp) => {\n if (comp.id === id) {\n version++;\n cb();\n }\n });\n const unsub2 = surface.componentsModel.onDeleted.subscribe((delId) => {\n if (delId === id) {\n version++;\n cb();\n }\n });\n return () => {\n unsub1.unsubscribe();\n unsub2.unsubscribe();\n };\n },\n getSnapshot: () => {\n const comp = surface.componentsModel.get(id);\n // We use instance identity + version as the snapshot to ensure\n // type replacements (e.g. Button -> Text) trigger a re-render.\n return comp ? `${comp.type}-${version}` : `missing-${version}`;\n },\n };\n }, [surface, id]);\n\n useSyncExternalStore(store.subscribe, store.getSnapshot);\n\n const componentModel = surface.componentsModel.get(id);\n\n if (!componentModel) {\n return (\n <div\n style={{\n padding: \"12px 16px\",\n borderRadius: \"8px\",\n background:\n \"linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%)\",\n backgroundSize: \"200% 100%\",\n animation: \"a2ui-shimmer 1.5s ease-in-out infinite\",\n minHeight: \"2rem\",\n }}\n >\n <style>{`@keyframes a2ui-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }`}</style>\n </div>\n );\n }\n\n const compImpl = surface.catalog.components.get(componentModel.type);\n\n if (!compImpl) {\n return (\n <div style={{ color: \"red\" }}>\n Unknown component: {componentModel.type}\n </div>\n );\n }\n\n return (\n <ResolvedChild\n surface={surface}\n id={id}\n basePath={basePath}\n componentModel={componentModel}\n compImpl={compImpl}\n />\n );\n});\nDeferredChild.displayName = \"DeferredChild\";\n\nexport const A2uiSurface: React.FC<{\n surface: SurfaceModel<ReactComponentImplementation>;\n}> = ({ surface }) => {\n // The root component always has ID 'root' and base path '/'\n return <DeferredChild surface={surface} id=\"root\" basePath=\"/\" />;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,gBAAgB,MACnB,EACC,SACA,IACA,UACA,UACA,qBAOI;CACJ,MAAM,oBAAoB,SAAS;CAGnC,MAAM,UAAU,cACR,IAAI,iBAAiB,SAAS,IAAI,SAAS,EAGjD;EAAC;EAAS;EAAI;EAAU;EAAe,CACxC;AAiBD,QAAO,oBAAC;EAA2B;EAAS,YAfzB,aAChB,SAAiB,iBAA0B;GAC1C,MAAM,OAAO,gBAAgB,QAAQ,YAAY;AACjD,UACE,oBAAC;IAEU;IACT,IAAI;IACJ,UAAU;MAHL,GAAG,QAAQ,GAAG,OAInB;KAGN,CAAC,SAAS,QAAQ,YAAY,KAAK,CACpC;GAEqE;EAEzE;AACD,cAAc,cAAc;AAE5B,MAAa,gBAIR,MAAM,EAAE,SAAS,IAAI,eAAe;CAEvC,MAAM,QAAQ,cAAc;EAC1B,IAAI,UAAU;AACd,SAAO;GACL,YAAY,OAAmB;IAC7B,MAAM,SAAS,QAAQ,gBAAgB,UAAU,WAAW,SAAS;AACnE,SAAI,KAAK,OAAO,IAAI;AAClB;AACA,UAAI;;MAEN;IACF,MAAM,SAAS,QAAQ,gBAAgB,UAAU,WAAW,UAAU;AACpE,SAAI,UAAU,IAAI;AAChB;AACA,UAAI;;MAEN;AACF,iBAAa;AACX,YAAO,aAAa;AACpB,YAAO,aAAa;;;GAGxB,mBAAmB;IACjB,MAAM,OAAO,QAAQ,gBAAgB,IAAI,GAAG;AAG5C,WAAO,OAAO,GAAG,KAAK,KAAK,GAAG,YAAY,WAAW;;GAExD;IACA,CAAC,SAAS,GAAG,CAAC;AAEjB,sBAAqB,MAAM,WAAW,MAAM,YAAY;CAExD,MAAM,iBAAiB,QAAQ,gBAAgB,IAAI,GAAG;AAEtD,KAAI,CAAC,eACH,QACE,oBAAC;EACC,OAAO;GACL,SAAS;GACT,cAAc;GACd,YACE;GACF,gBAAgB;GAChB,WAAW;GACX,WAAW;GACZ;YAED,oBAAC,qBAAO,2GAAiH;GACrH;CAIV,MAAM,WAAW,QAAQ,QAAQ,WAAW,IAAI,eAAe,KAAK;AAEpE,KAAI,CAAC,SACH,QACE,qBAAC;EAAI,OAAO,EAAE,OAAO,OAAO;aAAE,uBACR,eAAe;GAC/B;AAIV,QACE,oBAAC;EACU;EACL;EACM;EACM;EACN;GACV;EAEJ;AACF,cAAc,cAAc;AAE5B,MAAa,eAEP,EAAE,cAAc;AAEpB,QAAO,oBAAC;EAAuB;EAAS,IAAG;EAAO,UAAS;GAAM"}