@copilotkit/a2ui-renderer
Version:
A2UI Renderer for CopilotKit - render A2UI surfaces in React applications
129 lines (127 loc) • 4.16 kB
JavaScript
import { basicCatalog } from "../a2ui-react/catalog/basic/index.mjs";
import "../a2ui-react/index.mjs";
import { ThemeProvider } from "../theme/ThemeContext.mjs";
import { createContext, useContext, useMemo, useRef, useState } from "react";
import { MessageProcessor } from "@a2ui/web_core/v0_9";
import { jsx } from "react/jsx-runtime";
//#region src/react-renderer/core/A2UIProvider.tsx
/**
* Context for stable actions (never changes reference, prevents re-renders).
*/
const A2UIActionsContext = createContext(null);
/**
* Context for reactive state (changes trigger re-renders).
*/
const A2UIStateContext = createContext(null);
/**
* Provider component that sets up the A2UI v0.9 context for descendant components.
* Uses a two-context architecture for performance:
* - A2UIActionsContext: Stable actions that never change (no re-renders)
* - A2UIStateContext: Reactive state that triggers re-renders when needed
*/
function A2UIProvider({ onAction, theme, catalog, children }) {
const onActionRef = useRef(onAction ?? null);
onActionRef.current = onAction ?? null;
const processorRef = useRef(null);
if (!processorRef.current) processorRef.current = new MessageProcessor([catalog ?? basicCatalog], (action) => {
if (onActionRef.current) {
const message = { userAction: {
name: action?.name ?? "unknown",
surfaceId: action?.surfaceId ?? "default",
sourceComponentId: action?.sourceComponentId,
context: action?.context,
timestamp: action?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
} };
onActionRef.current(message);
}
});
const processor = processorRef.current;
const [version, setVersion] = useState(0);
const [error, setError] = useState(null);
const actionsRef = useRef(null);
if (!actionsRef.current) actionsRef.current = {
processMessages: (messages) => {
try {
processor.processMessages(messages);
} catch (err) {
console.warn("[A2UI] processMessages error:", err);
setError(err instanceof Error ? err.message : String(err));
return;
}
setError(null);
setVersion((v) => v + 1);
},
dispatch: (message) => {
if (onActionRef.current) onActionRef.current(message);
},
getSurface: (surfaceId) => {
return processor.model.getSurface(surfaceId);
},
clearSurfaces: () => {
const surfaces = processor.model.surfacesMap;
for (const [id] of surfaces) processor.processMessages([{
version: "v0.9",
deleteSurface: { surfaceId: id }
}]);
setVersion((v) => v + 1);
}
};
const actions = actionsRef.current;
const stateValue = useMemo(() => ({
version,
error
}), [version, error]);
return /* @__PURE__ */ jsx(A2UIActionsContext.Provider, {
value: actions,
children: /* @__PURE__ */ jsx(A2UIStateContext.Provider, {
value: stateValue,
children: /* @__PURE__ */ jsx(ThemeProvider, {
theme,
children
})
})
});
}
/**
* Hook to access stable A2UI actions (won't cause re-renders).
*/
function useA2UIActions() {
const actions = useContext(A2UIActionsContext);
if (!actions) throw new Error("useA2UIActions must be used within an A2UIProvider");
return actions;
}
/**
* Hook to subscribe to A2UI state changes.
*/
function useA2UIState() {
const state = useContext(A2UIStateContext);
if (!state) throw new Error("useA2UIState must be used within an A2UIProvider");
return state;
}
/**
* Hook to access the full A2UI context (actions + state).
*/
function useA2UIContext() {
const actions = useA2UIActions();
const state = useA2UIState();
return useMemo(() => ({
...actions,
version: state.version,
onAction: null
}), [actions, state.version]);
}
/** @deprecated Use useA2UIContext instead. */
const useA2UIStore = useA2UIContext;
/**
* Hook to access the current A2UI error state.
*/
function useA2UIError() {
return useContext(A2UIStateContext)?.error ?? null;
}
/** @deprecated Use useA2UIContext() or useA2UI() directly instead. */
function useA2UIStoreSelector(selector) {
return selector(useA2UIContext());
}
//#endregion
export { A2UIProvider, useA2UIActions, useA2UIContext, useA2UIError, useA2UIState, useA2UIStore, useA2UIStoreSelector };
//# sourceMappingURL=A2UIProvider.mjs.map