({ isVisible, getCtx });
+
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * Hook to access visibility evaluation
+ */
+export function useVisibility(): VisibilityContextValue {
+ const ctx = useContext(VisibilityContext);
+ if (!ctx) {
+ throw new Error("useVisibility must be used within a VisibilityProvider");
+ }
+ return ctx;
+}
+
+/**
+ * Hook to check if a condition is visible
+ */
+export function useIsVisible(
+ condition: VisibilityCondition | undefined,
+): boolean {
+ const { isVisible } = useVisibility();
+ return isVisible(condition);
+}
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/index.ts b/packages/pro-components/chat/chat-engine/components/json-render/index.ts
new file mode 100644
index 0000000000..cfa52695b9
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/index.ts
@@ -0,0 +1,52 @@
+/**
+ * json-render 集成模块入口
+ * 导出所有核心 API 和类型
+ */
+
+// ==================== 核心组件 ====================
+// 主要渲染器组件
+export {
+ JsonRenderActivityRenderer,
+ type JsonRenderActivityRendererProps,
+} from './renderer/JsonRenderActivityRenderer';
+
+// A2UI 渲染器组件
+export {
+ A2UIJsonRenderActivityRenderer,
+ type A2UIJsonRenderActivityRendererProps,
+} from './renderer/A2UIJsonRenderActivityRenderer';
+
+// A2UI Surface React 集成(自定义协议场景使用)
+export {
+ type A2UISurfaceController,
+ A2UISurfaceRenderer,
+ type A2UISurfaceRendererProps,
+ useA2UISurface,
+ type UseA2UISurfaceOptions,
+} from './renderer/A2UISurface';
+
+// ==================== 上下文 (Contexts) ====================
+export * from './contexts';
+
+// ==================== 注册表 (Registry) ====================
+export type { A2UIBindingConfig, CreateCustomRegistryOptions, JsonRenderActivityConfigOptions } from './registry';
+export {
+ A2UIButton,
+ a2uiRegistry,
+ A2UITextField,
+ createA2UIRegistry,
+ createCustomRegistry,
+ tdesignRegistry,
+ withA2UIBinding,
+ withStableProps,
+} from './registry';
+
+// ==================== 配置工厂 ====================
+export { createA2UIJsonRenderActivityConfig, createJsonRenderActivityConfig } from './registry';
+
+// ==================== 目录 (Catalog) ====================
+export * from './catalog/catalog-to-prompt';
+
+// ==================== 类型定义 ====================
+export type { JSONUIProviderProps } from './renderer/JsonUIRenderer';
+export type * from './types';
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/registry/a2ui-binding.tsx b/packages/pro-components/chat/chat-engine/components/json-render/registry/a2ui-binding.tsx
new file mode 100644
index 0000000000..3755e74f9e
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/registry/a2ui-binding.tsx
@@ -0,0 +1,265 @@
+/**
+ * A2UI 数据绑定 HOC
+ *
+ * 统一处理 A2UI 协议的标准字段:
+ * - valuePath: 值的数据绑定路径(如 /userInfo/name)
+ * - disabledPath: disabled 状态的数据绑定路径(如 /formDisabled)
+ * - action.context: action 参数中的动态数据绑定
+ *
+ * 性能优化:
+ * - 使用 React.memo + 精确值比较避免不必要渲染
+ * - Action 参数延迟解析(触发时才计算)
+ * - 使用 useRef 缓存回调函数避免重建
+ *
+ * 使用方式:
+ * ```tsx
+ * // 创建支持 A2UI 绑定的 Input
+ * const A2UIInput = withA2UIBinding(Input, {
+ * valueField: 'value',
+ * onChangeField: 'onChange',
+ * });
+ *
+ * // 创建支持 action 的 Button
+ * const A2UIButton = withA2UIBinding(Button, {
+ * supportsAction: true,
+ * actionTrigger: 'onClick', // 默认
+ * });
+ *
+ * // Input 支持 onEnter 触发 action
+ * const A2UISearchInput = withA2UIBinding(Input, {
+ * valueField: 'value',
+ * onChangeField: 'onChange',
+ * supportsAction: true,
+ * actionTrigger: 'onEnter',
+ * });
+ * ```
+ */
+
+import React, { memo, useCallback, useMemo, useRef } from 'react';
+import { normalizeActionBinding, resolveActionParams } from '@tdesign/ai-chat-engine';
+
+import { useDataBinding, useDataStore, useDataValue } from '..';
+
+import type { ActionBinding } from '@json-render/core';
+import type { ComponentRenderProps } from '../types';
+/**
+ * A2UI 绑定配置
+ */
+export interface A2UIBindingConfig {
+ /** 组件的值字段名,默认 'value' */
+ valueField?: string;
+ /** 组件的 onChange 字段名,默认 'onChange' */
+ onChangeField?: string;
+ /** 是否支持 action 绑定,默认 false */
+ supportsAction?: boolean;
+ /**
+ * Action 触发事件名,默认 'onClick'
+ * 可设置为 'onEnter'、'onChange' 等
+ */
+ actionTrigger?: string;
+}
+
+/**
+ * A2UI 数据绑定 HOC 内部组件
+ * 处理实际的数据绑定逻辑
+ */
+interface A2UIBoundInnerProps extends ComponentRenderProps {
+ WrappedComponent: React.ComponentType
;
+ valueField: string;
+ onChangeField: string;
+ supportsAction: boolean;
+ actionTrigger: string;
+}
+
+function A2UIBoundInner
>({
+ element,
+ children,
+ onAction,
+ WrappedComponent,
+ valueField,
+ onChangeField,
+ supportsAction,
+ actionTrigger,
+}: A2UIBoundInnerProps
) {
+ // 提取 A2UI 特有字段
+ const {
+ valuePath,
+ disabledPath,
+ action,
+ disabled: staticDisabled,
+ ...componentProps
+ } = element.props as P & {
+ valuePath?: string;
+ disabledPath?: string;
+ action?: string | ActionBinding;
+ disabled?: boolean;
+ };
+
+ // 细粒度订阅:只订阅需要的路径
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ const [boundValue, setBoundValue] = useDataBinding(valuePath!);
+ const disabledValue = useDataValue(disabledPath);
+
+ // 获取 store(用于 action 触发时读取最新 data)
+ const store = useDataStore();
+
+ // 使用 ref 缓存 store,action 触发时获取最新值
+ const storeRef = useRef(store);
+ storeRef.current = store;
+
+ // 计算 disabled 状态
+ const boundDisabled = useMemo(() => {
+ if (disabledPath) {
+ return Boolean(disabledValue);
+ }
+ return staticDisabled ?? false;
+ }, [disabledPath, disabledValue, staticDisabled]);
+
+ // 创建稳定的 onChange 处理器(useDataBinding 已返回稳定函数)
+ const handleChange = useCallback(
+ (newValue: unknown) => {
+ if (valuePath && setBoundValue) {
+ setBoundValue(newValue as any);
+ }
+ },
+ [valuePath, setBoundValue],
+ );
+
+ // 创建稳定的 action 处理器(延迟解析,触发时才获取最新 data)
+ const handleAction = useCallback(() => {
+ if (!action || !onAction) return;
+
+ // 协议适配:归一化 action 字段
+ // - 字符串简写:"submit"
+ // - 标准 ActionBinding:{ action, params? }
+ // - 兼容旧协议(A2UI / 旧版 mock 数据):{ name, context? }
+ const actionObj = normalizeActionBinding(action as any);
+
+ if (!actionObj) {
+ console.error(
+ '[withA2UIBinding] action 字段缺失或不符合 ActionBinding 协议(应为字符串或 { action, params? }),实际收到:',
+ action,
+ );
+ return;
+ }
+
+ // 使用最新的 data 解析参数
+ const currentData = storeRef.current.getData();
+ const resolvedParams = actionObj.params
+ ? resolveActionParams(actionObj.params as Record, currentData)
+ : {};
+
+ const resolvedAction: ActionBinding = {
+ ...actionObj,
+ params: resolvedParams,
+ };
+
+ onAction(resolvedAction);
+ }, [action, onAction]);
+
+ // 构建最终 props
+ const finalProps = useMemo(() => {
+ const props: any = {
+ ...componentProps,
+ disabled: boundDisabled,
+ };
+
+ // 如果有 valuePath,注入值和 onChange
+ if (valuePath !== undefined) {
+ props[valueField] = boundValue;
+ props[onChangeField] = handleChange;
+ }
+
+ // 如果支持 action,注入到指定的触发事件
+ if (supportsAction && action) {
+ const originalHandler = componentProps[actionTrigger];
+ props[actionTrigger] = (...args: any[]) => {
+ // 先调用原始处理器
+ if (typeof originalHandler === 'function') {
+ originalHandler(...args);
+ }
+ // 再触发 action
+ handleAction();
+ };
+ }
+
+ return props;
+ }, [
+ componentProps,
+ boundDisabled,
+ valuePath,
+ valueField,
+ boundValue,
+ onChangeField,
+ handleChange,
+ supportsAction,
+ action,
+ actionTrigger,
+ handleAction,
+ ]);
+
+ return {children};
+}
+
+/**
+ * A2UI 数据绑定 HOC
+ *
+ * 自动处理 A2UI 协议的标准字段,让原子组件保持纯净
+ *
+ * @param WrappedComponent 原始组件
+ * @param config 绑定配置
+ */
+export function withA2UIBinding>(
+ WrappedComponent: React.ComponentType
,
+ config: A2UIBindingConfig = {},
+): React.FC {
+ const {
+ valueField = 'value',
+ onChangeField = 'onChange',
+ supportsAction = false,
+ actionTrigger = 'onClick',
+ } = config;
+
+ const A2UIBoundComponent: React.FC = (props) => (
+
+ );
+
+ A2UIBoundComponent.displayName = `withA2UIBinding(${
+ WrappedComponent.displayName || WrappedComponent.name || 'Component'
+ })`;
+
+ // 使用 memo 包装,通过精确比较避免不必要渲染
+ return memo(A2UIBoundComponent, (prevProps, nextProps) => {
+ // element 引用相同,跳过渲染
+ if (prevProps.element === nextProps.element) return true;
+
+ // 比较关键字段
+ const prevEl = prevProps.element;
+ const nextEl = nextProps.element;
+
+ if (prevEl.type !== nextEl.type) return false;
+
+ // 比较 props(浅比较)
+ const prevElProps = prevEl.props || {};
+ const nextElProps = nextEl.props || {};
+ const prevKeys = Object.keys(prevElProps);
+ const nextKeys = Object.keys(nextElProps);
+
+ if (prevKeys.length !== nextKeys.length) return false;
+
+ for (const key of prevKeys) {
+ if (prevElProps[key] !== nextElProps[key]) return false;
+ }
+
+ return true;
+ });
+}
+
+export default withA2UIBinding;
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/registry/a2ui-registry.tsx b/packages/pro-components/chat/chat-engine/components/json-render/registry/a2ui-registry.tsx
new file mode 100644
index 0000000000..634aa3bfac
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/registry/a2ui-registry.tsx
@@ -0,0 +1,158 @@
+/**
+ * A2UI 专用组件注册表
+ *
+ * 使用 withA2UIBinding HOC 包装原子组件,自动处理 A2UI 协议字段:
+ * - valuePath: 值的数据绑定(如 /userInfo/name)
+ * - disabledPath: disabled 状态的数据绑定(如 /formDisabled)
+ * - action.context: action 参数中的动态数据绑定({ path: '/xxx' } 格式)
+ *
+ * 与 tdesignRegistry 的区别:
+ * - tdesignRegistry: 纯净的 TDesign 组件,用于直接的 json-render schema
+ * - a2uiRegistry: 支持 A2UI 协议的组件,用于 A2UI → json-render 转换后的渲染
+ *
+ * 使用方式:
+ * ```tsx
+ * // 业务使用 A2UI 协议时
+ * const config = createA2UIJsonRenderActivityConfig({
+ * registry: a2uiRegistry, // 内置 A2UI 组件
+ * // 或扩展自定义组件
+ * registry: createA2UIRegistry({
+ * MyCustomComponent: withA2UIBinding(MyComponent, { supportsAction: true }),
+ * }),
+ * });
+ * ```
+ */
+
+import React from 'react';
+import { Button, Input, Space } from 'tdesign-react';
+
+// 导入纯净组件和布局组件(这些不需要 A2UI 绑定)
+import { JsonRenderCard } from '../catalog/atomic/card';
+import {
+ JsonRenderCol,
+ JsonRenderColumn,
+ JsonRenderDivider,
+ JsonRenderRow,
+ JsonRenderSpace,
+} from '../catalog/atomic/layout';
+import { JsonRenderText } from '../catalog/atomic/text';
+import { withA2UIBinding } from './a2ui-binding';
+
+import type { ButtonProps, InputProps } from 'tdesign-react';
+import type { ComponentRegistry } from '../types';
+
+// ==================== 基础组件包装器 ====================
+
+/**
+ * 基础 Input 组件(用于 HOC 包装)
+ * 接收标准 InputProps,由 HOC 注入 value/onChange/disabled
+ */
+const BaseInput: React.FC = ({ label, ...props }) => {
+ if (label) {
+ return (
+
+
+
+
+ );
+ }
+ return ;
+};
+
+BaseInput.displayName = 'BaseInput';
+
+/**
+ * 基础 Button 组件(用于 HOC 包装)
+ * 接收标准 ButtonProps,由 HOC 注入 onClick/disabled
+ */
+const BaseButton: React.FC = ({ label, children, ...props }) => {
+ const content = label || children;
+ return ;
+};
+
+BaseButton.displayName = 'BaseButton';
+
+// ==================== A2UI 组件(通过 HOC 生成)====================
+
+/**
+ * A2UI TextField 组件
+ * 自动支持 valuePath/disabledPath 数据绑定
+ */
+export const A2UITextField = withA2UIBinding(BaseInput, {
+ valueField: 'value',
+ onChangeField: 'onChange',
+ supportsAction: false,
+});
+
+/**
+ * A2UI Button 组件
+ * 自动支持 disabledPath 和 action.context 动态绑定
+ */
+export const A2UIButton = withA2UIBinding(BaseButton, {
+ supportsAction: true,
+});
+
+// ==================== A2UI Registry ====================
+
+/**
+ * A2UI 专用组件注册表
+ *
+ * 用于 A2UI 协议转换后的渲染,组件自动支持:
+ * - valuePath: 值绑定到 dataModel
+ * - disabledPath: disabled 状态绑定到 dataModel
+ * - action.context: action 参数动态解析
+ *
+ * @example
+ * ```tsx
+ * import { a2uiRegistry } from './catalog/a2ui-registry';
+ *
+ * const config = createA2UIJsonRenderActivityConfig({
+ * registry: a2uiRegistry, // 使用 A2UI 专用 registry
+ * actionHandlers: { ... },
+ * });
+ * ```
+ */
+export const a2uiRegistry: ComponentRegistry = {
+ // A2UI 绑定组件(通过 HOC 包装)
+ TextField: A2UITextField,
+ Button: A2UIButton,
+
+ // 纯净组件(布局类不需要 A2UI 绑定)
+ Card: JsonRenderCard,
+ Text: JsonRenderText,
+ Row: JsonRenderRow,
+ Col: JsonRenderCol,
+ Space: JsonRenderSpace,
+ Column: JsonRenderColumn,
+ Divider: JsonRenderDivider,
+};
+
+/**
+ * 创建自定义 A2UI 组件注册表
+ *
+ * 基于 a2uiRegistry 扩展自定义组件
+ * 自定义组件如需支持 A2UI 协议,请使用 withA2UIBinding 包装
+ *
+ * @example
+ * ```tsx
+ * import { createA2UIRegistry, withA2UIBinding } from '@tdesign-react/chat';
+ *
+ * // 创建支持 A2UI 的自定义组件
+ * const A2UIDatePicker = withA2UIBinding(DatePicker, {
+ * valueField: 'value',
+ * onChangeField: 'onChange',
+ * });
+ *
+ * const customRegistry = createA2UIRegistry({
+ * DatePicker: A2UIDatePicker,
+ * });
+ * ```
+ */
+export function createA2UIRegistry(customComponents: ComponentRegistry): ComponentRegistry {
+ return {
+ ...a2uiRegistry,
+ ...customComponents,
+ };
+}
+
+export default a2uiRegistry;
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/registry/config.tsx b/packages/pro-components/chat/chat-engine/components/json-render/registry/config.tsx
new file mode 100644
index 0000000000..b9f045fea5
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/registry/config.tsx
@@ -0,0 +1,186 @@
+/**
+ * json-render Activity 配置工厂函数
+ * 便捷创建 ActivityConfig 用于注册
+ */
+
+import React from 'react';
+
+import { A2UIJsonRenderActivityRenderer } from '../renderer/A2UIJsonRenderActivityRenderer';
+import { JsonRenderActivityRenderer } from '../renderer/JsonRenderActivityRenderer';
+import { a2uiRegistry, tdesignRegistry } from '.';
+
+import type { ActivityConfig } from '../../activity/types';
+import type { ComponentRegistry,JsonRenderActivityProps } from '../types';
+
+/**
+ * json-render Activity 配置选项
+ */
+export interface JsonRenderActivityConfigOptions {
+ /** Activity 类型标识,默认 'json-render' */
+ activityType?: string;
+ /** 组件注册表,默认使用 tdesignRegistry */
+ registry?: ComponentRegistry;
+ /**
+ * Action 处理器映射表
+ *
+ * 重要:json-render 采用预定义 action 模式
+ * - AI/服务端只能生成在此预定义的 action 名称
+ * - 每个 action 对应一个具体的处理函数
+ * - 这确保了生成式 UI 的安全性和可控性
+ *
+ * @example
+ * ```tsx
+ * actionHandlers: {
+ * // 表单提交
+ * submit: async (params) => {
+ * await api.submitForm(params);
+ * MessagePlugin.success('提交成功');
+ * },
+ *
+ * // 表单重置
+ * reset: async (params) => {
+ * MessagePlugin.info('表单已重置');
+ * },
+ *
+ * // 删除操作
+ * delete: async (params) => {
+ * await api.deleteItem(params.id);
+ * },
+ *
+ * // 刷新数据
+ * refresh: async () => {
+ * await refetchData();
+ * }
+ * }
+ * ```
+ */
+ actionHandlers?: Record) => void | Promise>;
+ /** 显示调试信息 */
+ debug?: boolean;
+ /** 描述信息 */
+ description?: string;
+}
+
+/**
+ * 创建 json-render Activity 配置
+ *
+ * @example
+ * 基础用法 - 预定义 action handlers
+ * ```tsx
+ * const jsonRenderConfig = createJsonRenderActivityConfig({
+ * activityType: 'json-render',
+ * actionHandlers: {
+ * submit: async (params) => {
+ * console.log('提交表单:', params);
+ * await api.submit(params);
+ * MessagePlugin.success('提交成功');
+ * },
+ * reset: async (params) => {
+ * MessagePlugin.info('表单已重置');
+ * },
+ * delete: async (params) => {
+ * await api.delete(params.id);
+ * }
+ * },
+ * });
+ *
+ * useAgentActivity(jsonRenderConfig);
+ * ```
+ *
+ * @example
+ * 结合 ChatEngine 发送消息到服务端
+ * ```tsx
+ * const jsonRenderConfig = createJsonRenderActivityConfig({
+ * actionHandlers: {
+ * submit: async (params) => {
+ * // 发送到服务端处理
+ * await chatEngine.sendAIMessage({
+ * params: { userActionMessage: { action: 'submit', params } },
+ * sendRequest: true,
+ * });
+ * },
+ * reset: async (params) => {
+ * // 本地处理,不发送到服务端
+ * MessagePlugin.info('已重置');
+ * },
+ * },
+ * });
+ * ```
+ *
+ * @example
+ * 配合自定义组件注册表
+ * ```tsx
+ * import { createCustomRegistry } from './catalog';
+ *
+ * const jsonRenderConfig = createJsonRenderActivityConfig({
+ * registry: createCustomRegistry({
+ * MyCustomComponent: MyComponentRenderer,
+ * }),
+ * actionHandlers: {
+ * custom_action: async (params) => {
+ * // 处理自定义操作
+ * },
+ * },
+ * });
+ * ```
+ */
+export function createJsonRenderActivityConfig(
+ options: JsonRenderActivityConfigOptions = {},
+): ActivityConfig {
+ const {
+ activityType = 'json-render',
+ registry = tdesignRegistry,
+ actionHandlers = {},
+ debug = false,
+ description = 'json-render 动态 UI 渲染器',
+ } = options;
+
+ return {
+ activityType,
+ component: React.memo((props: JsonRenderActivityProps) => (
+
+ )),
+ description,
+ };
+}
+
+/**
+ * 创建 A2UI + json-render Activity 配置
+ * 支持将 A2UI 协议转换为 json-render Schema 渲染
+ *
+ * 注意:默认使用 a2uiRegistry,自动支持 valuePath/disabledPath/action.context 绑定
+ *
+ * @example
+ * ```tsx
+ * const a2uiJsonRenderConfig = createA2UIJsonRenderActivityConfig({
+ * activityType: 'a2ui-json-render',
+ * actionHandlers: {
+ * submit: async (params) => {
+ * console.log('提交:', params);
+ * },
+ * cancel: async (params) => {
+ * console.log('取消');
+ * },
+ * },
+ * });
+ *
+ * useAgentActivity(a2uiJsonRenderConfig);
+ * ```
+ */
+export function createA2UIJsonRenderActivityConfig(options: JsonRenderActivityConfigOptions = {}): ActivityConfig {
+ const {
+ activityType = 'a2ui-json-render',
+ registry = a2uiRegistry, // A2UI 默认使用 a2uiRegistry
+ actionHandlers = {},
+ debug = false,
+ description = 'A2UI + json-render 适配渲染器',
+ } = options;
+
+ return {
+ activityType,
+ component: React.memo((props: any) => (
+
+ )),
+ description,
+ };
+}
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/registry/index.ts b/packages/pro-components/chat/chat-engine/components/json-render/registry/index.ts
new file mode 100644
index 0000000000..5daed741b4
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/registry/index.ts
@@ -0,0 +1,234 @@
+/**
+ * TDesign ComponentRegistry(React 组件注册表)
+ * 用于 json-render 渲染层的组件映射
+ *
+ * 重要概念区分:
+ * - ComponentRegistry(本文件):渲染层,映射组件名到 React 组件(传给 Renderer)
+ * - Catalog(catalog.ts):约束层,定义组件 props schema 和 actions 白名单(给 AI/服务端)
+ *
+ * Registry 分类:
+ * - tdesignRegistry: 纯净的 TDesign 组件,用于直接的 json-render schema
+ * - a2uiRegistry: 支持 A2UI 协议的组件,自动处理 valuePath/disabledPath/action.context
+ *
+ * 详见:ARCHITECTURE.md
+ */
+
+import React from 'react';
+import isEqual from 'react-fast-compare';
+import { JsonRenderButton } from '../catalog/atomic/button';
+import { JsonRenderInput, JsonRenderTextField } from '../catalog/atomic/input';
+import { JsonRenderCard } from '../catalog/atomic/card';
+import { JsonRenderText } from '../catalog/atomic/text';
+import {
+ JsonRenderRow,
+ JsonRenderCol,
+ JsonRenderSpace,
+ JsonRenderColumn,
+ JsonRenderDivider,
+} from '../catalog/atomic/layout';
+import type { ComponentRegistry, ComponentRenderProps } from '../types';
+
+/**
+ * 高性能组件包装器(可选)
+ * 使用 React.memo + react-fast-compare 实现深比较
+ *
+ * 注意:由于 ElementRenderer 已经实现了 React.memo + 深比较优化,
+ * 大多数情况下叶子组件不需要再使用 withStableProps 包装。
+ *
+ * 使用场景:
+ * - 组件内部有复杂的计算逻辑,希望进一步减少重渲染
+ * - 组件使用了 Context,需要避免 Context 变化导致的不必要渲染
+ *
+ * 原理:
+ * - json-render 每次渲染都会创建新的 element 对象引用
+ * - 默认的 React.memo 浅比较会认为 props 变化了
+ * - 使用 react-fast-compare 进行高效深比较,只在内容真正变化时才重渲染
+ *
+ * 性能说明:
+ * - react-fast-compare 比 JSON.stringify 更快(短路比较)
+ * - 发现第一个不同属性时立即停止,不会遍历整个对象
+ * - 处理了循环引用等边缘情况
+ */
+export function withStableProps(
+ Component: React.ComponentType
,
+): React.MemoExoticComponent> {
+ return React.memo(Component, (prevProps, nextProps) => {
+ const prevElement = prevProps.element as any;
+ const nextElement = nextProps.element as any;
+
+ // 1. children 变化必须重渲染
+ // 深层更新时,父组件的 element 可能不变,但 children(子组件树)会变化
+ if (prevProps.children !== nextProps.children) {
+ return false;
+ }
+
+ // 2. element 引用相同,跳过渲染
+ if (prevElement === nextElement) return true;
+
+ // 3. 快速路径:id 或 type 不同,需要重渲染
+ if (prevElement.id !== nextElement.id || prevElement.type !== nextElement.type) {
+ return false;
+ }
+
+ // 4. 使用 react-fast-compare 进行高效深比较
+ return isEqual(prevElement.props, nextElement.props);
+ });
+}
+
+/**
+ * TDesign 内置组件注册表(渲染层)
+ *
+ * 这是框架内置的原子组件集合,提供基础 UI 渲染能力
+ * 业务层可以通过 createCustomRegistry 扩展自定义组件
+ *
+ * 使用方式:
+ * ```tsx
+ * import { tdesignRegistry } from '@tdesign-react/chat';
+ *
+ * const config = createJsonRenderActivityConfig({
+ * registry: tdesignRegistry,
+ * actionHandlers: { ... },
+ * });
+ * ```
+ *
+ * Schema 示例:
+ * ```json
+ * {
+ * "root": "btn1",
+ * "elements": {
+ * "btn1": {
+ * "key": "btn1",
+ * "type": "Button",
+ * "props": {
+ * "variant": "base",
+ * "theme": "primary",
+ * "children": "点击我",
+ * "action": "submit"
+ * }
+ * }
+ * }
+ * }
+ * ```
+ */
+export const tdesignRegistry: ComponentRegistry = {
+ // 基础组件
+ Button: JsonRenderButton,
+ Input: JsonRenderInput,
+ TextField: JsonRenderTextField,
+ Card: JsonRenderCard,
+ Text: JsonRenderText,
+
+ // 布局组件
+ Row: JsonRenderRow,
+ Col: JsonRenderCol,
+ Space: JsonRenderSpace,
+ Column: JsonRenderColumn,
+ Divider: JsonRenderDivider,
+
+ // 别名(兼容不同命名风格)
+ button: JsonRenderButton,
+ input: JsonRenderInput,
+ textfield: JsonRenderTextField,
+ card: JsonRenderCard,
+ text: JsonRenderText,
+ row: JsonRenderRow,
+ col: JsonRenderCol,
+ space: JsonRenderSpace,
+ column: JsonRenderColumn,
+ divider: JsonRenderDivider,
+};
+
+/**
+ * createCustomRegistry 配置选项
+ */
+export interface CreateCustomRegistryOptions {
+ /**
+ * 是否自动包装组件以优化性能
+ * 使用 React.memo + react-fast-compare 深比较 element.props
+ *
+ * 注意:由于 ElementRenderer 已经实现了 memo 优化,
+ * 默认关闭此选项。仅在组件有复杂内部逻辑时考虑开启。
+ *
+ * @default false
+ */
+ enableStableProps?: boolean;
+}
+
+/**
+ * 创建自定义组件注册表(扩展内置组件)
+ *
+ * 用于渲染层:扩展自定义业务组件的 React 实现
+ *
+ * 性能说明:
+ * - ElementRenderer 已经使用 React.memo + 深比较优化,会自动跳过无变化节点
+ * - 默认情况下,自定义组件无需额外的 memo 包装
+ * - 如果组件有复杂内部逻辑,可以设置 enableStableProps: true 进行双重优化
+ *
+ * @example
+ * ```tsx
+ * import { createCustomRegistry } from '@tdesign-react/chat';
+ * import type { ComponentRenderProps } from '@json-render/react';
+ *
+ * // 定义自定义组件(无需手动 React.memo)
+ * const StatusCard: React.FC = ({ element }) => (
+ * {element.props.status}
+ * );
+ *
+ * const ProgressBar: React.FC = ({ element }) => (
+ *
+ * );
+ *
+ * // 扩展 registry(ElementRenderer 已有优化,无需额外包装)
+ * const customRegistry = createCustomRegistry({
+ * StatusCard,
+ * ProgressBar,
+ * });
+ *
+ * // 如果组件有复杂内部逻辑,可以开启双重优化
+ * const customRegistry = createCustomRegistry(
+ * { StatusCard, ProgressBar },
+ * { enableStableProps: true }
+ * );
+ * ```
+ *
+ * 注意:
+ * - 这里只定义渲染层的组件映射
+ * - 约束层(Catalog)需要使用 createCustomCatalog 定义(见 catalog.ts)
+ * - 两者需要保持组件名称一致
+ */
+export function createCustomRegistry(
+ customComponents: ComponentRegistry,
+ options: CreateCustomRegistryOptions = {},
+): ComponentRegistry {
+ const { enableStableProps = false } = options;
+
+ // 如果启用性能优化,自动包装组件
+ const processedComponents: ComponentRegistry = {};
+
+ if (enableStableProps) {
+ for (const [name, Component] of Object.entries(customComponents)) {
+ processedComponents[name] = withStableProps(Component as React.ComponentType);
+ }
+ } else {
+ Object.assign(processedComponents, customComponents);
+ }
+
+ return {
+ ...tdesignRegistry,
+ ...processedComponents,
+ };
+}
+
+// ==================== 重新导出 A2UI Registry ====================
+// A2UI 专用组件,支持 valuePath/disabledPath/action.context 自动绑定
+export { a2uiRegistry, createA2UIRegistry, A2UITextField, A2UIButton } from './a2ui-registry';
+
+
+// 配置工厂
+export type { JsonRenderActivityConfigOptions } from './config';
+// 默认导出配置函数
+export { createJsonRenderActivityConfig, createA2UIJsonRenderActivityConfig } from './config';
+
+// ==================== 重新导出 A2UI Binding HOC ====================
+export { withA2UIBinding } from './a2ui-binding';
+export type { A2UIBindingConfig } from './a2ui-binding';
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/renderer/A2UIJsonRenderActivityRenderer.tsx b/packages/pro-components/chat/chat-engine/components/json-render/renderer/A2UIJsonRenderActivityRenderer.tsx
new file mode 100644
index 0000000000..4649c64a49
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/renderer/A2UIJsonRenderActivityRenderer.tsx
@@ -0,0 +1,235 @@
+/**
+ * A2UI v0.9.1 + json-render Activity 渲染器
+ * 将 A2UI v0.9.1 协议转换为 json-render Schema 进行渲染
+ *
+ * 工作流程:
+ * 1. 接收 ACTIVITY_SNAPSHOT/DELTA 中的 A2UI content
+ * 2. 区分消息类型:UI型 vs 纯数据型
+ * 3. UI型:转换为 Schema 并注册到 SurfaceStateManager,渲染 UI
+ * 4. 纯数据型:通过 SurfaceStateManager 更新数据,触发订阅者重渲染,本组件不渲染
+ *
+ * 消息分类:
+ * - UI型消息:包含 createSurface / updateComponents / deleteSurface → 需要渲染/更新 UI
+ * - 纯数据型消息:仅包含 updateDataModel → 只更新状态,不渲染新 UI
+ */
+
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import {
+ convertA2UIMessagesToJsonRender,
+ extractSurfaceId,
+ hasCreationMessages,
+ hasDeletionMessages,
+ isUIMessages,
+ surfaceStateManager,
+} from '@tdesign/ai-chat-engine';
+
+import { JsonRenderActivityRenderer } from './JsonRenderActivityRenderer';
+
+import type { A2UIMessage, JsonRenderSchema } from '@tdesign/ai-chat-engine';
+import type { ComponentRegistry, JsonRenderActivityProps } from '../types';
+
+export interface A2UIJsonRenderActivityRendererProps extends Omit {
+ /** A2UI content(包含 messages 数组) */
+ content: {
+ messages?: A2UIMessage[];
+ [key: string]: any;
+ };
+ /** 组件注册表(必须) */
+ registry: ComponentRegistry;
+ /** Action 处理器(可选) */
+ actionHandlers?: Record) => void | Promise>;
+ /** 显示调试信息 */
+ debug?: boolean;
+}
+
+/**
+ * A2UI v0.9.1 + json-render Activity 渲染器组件
+ */
+export const A2UIJsonRenderActivityRenderer: React.FC = ({
+ activityType,
+ content,
+ messageId,
+ ext,
+ registry,
+ actionHandlers,
+ debug = false,
+}) => {
+ // 设置调试模式
+ useEffect(() => {
+ surfaceStateManager.setDebug(debug);
+ }, [debug]);
+
+ // 用于触发重渲染的版本号
+ const [schemaVersion, setSchemaVersion] = useState(0);
+
+ // 分析消息类型
+ const { surfaceId, isUI, isDeletion, isCreation, messages } = useMemo(() => {
+ const msgs = content.messages;
+ if (!Array.isArray(msgs) || msgs.length === 0) {
+ return { surfaceId: null, isUI: false, isDeletion: false, isCreation: false, messages: [] };
+ }
+ return {
+ surfaceId: extractSurfaceId(msgs),
+ isUI: isUIMessages(msgs),
+ isDeletion: hasDeletionMessages(msgs),
+ isCreation: hasCreationMessages(msgs),
+ messages: msgs,
+ };
+ }, [content.messages]);
+
+ // 处理消息并获取 Schema
+ const initialSchema = useMemo(() => {
+ if (!surfaceId || messages.length === 0) {
+ return null;
+ }
+
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 处理消息:', {
+ messageId,
+ surfaceId,
+ messagesCount: messages.length,
+ messageTypes: messages.map(
+ (m) =>
+ Object.keys(m).filter((k) =>
+ ['createSurface', 'updateComponents', 'updateDataModel', 'deleteSurface'].includes(k),
+ )[0],
+ ),
+ isUI,
+ isCreation,
+ isDeletion,
+ cachedSurfaces: surfaceStateManager.getAllSurfaceIds(),
+ });
+ }
+
+ // 删除型消息:清理缓存
+ if (isDeletion) {
+ surfaceStateManager.deleteSurface(surfaceId);
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 删除 Surface:', surfaceId);
+ }
+ return null;
+ }
+
+ // 创建型消息:转换并注册
+ if (isCreation) {
+ const schema = convertA2UIMessagesToJsonRender(messages);
+ if (schema) {
+ // 提取 catalogId
+ const catalogId = messages.find((m) => m.createSurface)?.createSurface?.catalogId;
+ surfaceStateManager.registerSurface(surfaceId, schema, catalogId);
+
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 创建型消息,注册 Surface:', {
+ surfaceId,
+ elementsCount: Object.keys(schema.elements).length,
+ data: schema.data,
+ });
+ }
+ }
+ return schema;
+ }
+
+ // 更新型消息:通过 SurfaceStateManager 更新数据
+ for (const msg of messages) {
+ if (msg.updateDataModel) {
+ const { path, op, value } = msg.updateDataModel;
+ const success = surfaceStateManager.updateData(surfaceId, path, op || 'replace', value);
+
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 更新型消息,更新数据:', {
+ surfaceId,
+ path,
+ op: op || 'replace',
+ value,
+ success,
+ });
+ }
+ }
+ }
+
+ // 更新型消息不需要渲染,返回 null
+ return null;
+ }, [messages, surfaceId, isCreation, isDeletion, isUI, debug, messageId]);
+
+ // 订阅状态变化的回调
+ const handleSchemaUpdate = useCallback(() => {
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 收到状态更新通知,触发重渲染');
+ }
+ setSchemaVersion((v) => v + 1);
+ }, [debug]);
+
+ // 订阅 Surface 状态变化(仅当是创建型消息时)
+ useEffect(() => {
+ if (!isCreation || !surfaceId) {
+ return;
+ }
+
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 订阅 Surface 状态:', surfaceId);
+ }
+
+ const unsubscribe = surfaceStateManager.subscribe(surfaceId, handleSchemaUpdate);
+
+ return () => {
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 取消订阅 Surface 状态:', surfaceId);
+ }
+ unsubscribe();
+ };
+ }, [isCreation, surfaceId, handleSchemaUpdate, debug]);
+
+ // 获取当前 Schema(考虑版本变化)
+ const currentSchema = useMemo(() => {
+ if (!isCreation || !surfaceId) {
+ return null;
+ }
+ // schemaVersion 变化时从缓存获取最新 Schema
+ return surfaceStateManager.getSchema(surfaceId) || initialSchema;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isCreation, surfaceId, initialSchema, schemaVersion]);
+
+ // 非 UI 型消息不渲染(仅 updateDataModel 的消息)
+ if (!isUI) {
+ if (debug && messages.length > 0) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 纯数据更新消息,跳过渲染');
+ }
+ return null;
+ }
+
+ // 删除型消息:返回 null(UI 已被删除)
+ if (isDeletion && !isCreation) {
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[A2UI Adapter] 删除型消息,跳过渲染');
+ }
+ return null;
+ }
+
+ // schema 尚未就绪:等待下一次更新
+ if (!currentSchema) {
+ return null;
+ }
+
+ // 渲染 UI
+ return (
+
+ );
+};
+
+export default A2UIJsonRenderActivityRenderer;
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/renderer/A2UISurface.tsx b/packages/pro-components/chat/chat-engine/components/json-render/renderer/A2UISurface.tsx
new file mode 100644
index 0000000000..12ebe3cffb
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/renderer/A2UISurface.tsx
@@ -0,0 +1,268 @@
+/**
+ * A2UI Surface React 集成
+ *
+ * 在 React 端提供两类 API:
+ * 1. useA2UISurface:管理一组 A2UI Surface 的生命周期,处理 A2UI v0.9.1 消息流
+ * 2. A2UISurfaceRenderer:渲染指定 surfaceId 的 UI,订阅 surfaceStateManager 状态变化
+ *
+ * 设计原则:
+ * - 协议解析 / Surface 状态管理:复用 ai-core 的 json-render 适配器(surfaceStateManager + convertA2UIMessagesToJsonRender)
+ * - React 相关逻辑(hook / 订阅 / 渲染):实现在 react 仓库
+ * - 不再依赖已废弃的 adapters/a2ui 模块
+ */
+
+import React, { useCallback, useMemo, useRef, useSyncExternalStore } from 'react';
+import {
+ applyA2UIDataUpdate,
+ applyA2UIUpdates,
+ convertA2UIMessagesToJsonRender,
+ groupMessagesBySurface,
+ surfaceStateManager,
+} from '@tdesign/ai-chat-engine';
+
+import { JsonRenderActivityRenderer } from './JsonRenderActivityRenderer';
+
+import type { A2UIMessage, JsonRenderSchema } from '@tdesign/ai-chat-engine';
+import type { ComponentRegistry } from '../types';
+
+/* ------------------------------------------------------------------ */
+/* A2UI Surface hook */
+/* ------------------------------------------------------------------ */
+
+/**
+ * useA2UISurface hook 返回值
+ */
+export interface A2UISurfaceController {
+ /** 当前活跃的 Surface ID 列表(已按出现顺序排列) */
+ surfaceIds: string[];
+ /** 处理一批 A2UI v0.9.1 消息(createSurface / updateComponents / updateDataModel / deleteSurface) */
+ processMessages: (messages: A2UIMessage[]) => void;
+ /** 清除所有 Surface 缓存与本地记录 */
+ clearAllSurfaces: () => void;
+ /** 检查指定 Surface 是否存在 */
+ hasSurface: (surfaceId: string) => boolean;
+}
+
+/**
+ * useA2UISurface 配置
+ */
+export interface UseA2UISurfaceOptions {
+ /** 是否打印调试日志 */
+ debug?: boolean;
+}
+
+/**
+ * 管理一组 A2UI Surface 的生命周期
+ *
+ * 内部状态:
+ * - 仅维护"哪些 surfaceId 是当前 hook 创建的"这一份本地快照(不存 schema,避免与 surfaceStateManager 双源)
+ * - 真正的 schema/data 由 surfaceStateManager 持有,通过 A2UISurfaceRenderer 订阅渲染
+ */
+export function useA2UISurface(options: UseA2UISurfaceOptions = {}): A2UISurfaceController {
+ const { debug = false } = options;
+
+ // 当前 hook 持有的 surfaceId 集合(用版本号驱动 useSyncExternalStore 重渲染)
+ const surfaceIdsRef = useRef([]);
+ const versionRef = useRef(0);
+ const listenersRef = useRef void>>(new Set());
+
+ const subscribe = useCallback((listener: () => void) => {
+ listenersRef.current.add(listener);
+ return () => {
+ listenersRef.current.delete(listener);
+ };
+ }, []);
+
+ // 缓存版本相关的快照对象,避免无限重渲染
+ const snapshotRef = useRef<{ ids: string[]; version: number }>({
+ ids: surfaceIdsRef.current,
+ version: versionRef.current,
+ });
+ const getSnapshot = useCallback(() => {
+ if (snapshotRef.current.version !== versionRef.current) {
+ snapshotRef.current = { ids: surfaceIdsRef.current.slice(), version: versionRef.current };
+ }
+ return snapshotRef.current;
+ }, []);
+
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
+
+ const notify = useCallback(() => {
+ versionRef.current += 1;
+ listenersRef.current.forEach((listener) => listener());
+ }, []);
+
+ const addSurfaceId = useCallback(
+ (surfaceId: string) => {
+ if (!surfaceIdsRef.current.includes(surfaceId)) {
+ surfaceIdsRef.current = [...surfaceIdsRef.current, surfaceId];
+ notify();
+ }
+ },
+ [notify],
+ );
+
+ const removeSurfaceId = useCallback(
+ (surfaceId: string) => {
+ if (surfaceIdsRef.current.includes(surfaceId)) {
+ surfaceIdsRef.current = surfaceIdsRef.current.filter((id) => id !== surfaceId);
+ notify();
+ }
+ },
+ [notify],
+ );
+
+ /**
+ * 处理一批 A2UI v0.9.1 消息
+ *
+ * 路由策略:
+ * - createSurface + updateComponents(同批):调用 convertA2UIMessagesToJsonRender 一次性产出 schema 并 registerSurface
+ * - 已存在 surface 上的 updateComponents:调用 applyA2UIUpdates 增量更新现有 schema
+ * - updateDataModel:通过 surfaceStateManager.updateData 走标准订阅通知路径
+ * - deleteSurface:调用 surfaceStateManager.deleteSurface 并从本地列表移除
+ */
+ const processMessages = useCallback(
+ (messages: A2UIMessage[]) => {
+ if (!Array.isArray(messages) || messages.length === 0) return;
+
+ const grouped = groupMessagesBySurface(messages);
+
+ grouped.forEach((surfaceMessages, surfaceId) => {
+ // 先处理删除:删除后该批后续消息无意义
+ const hasDelete = surfaceMessages.some((msg) => msg.deleteSurface);
+ if (hasDelete) {
+ surfaceStateManager.deleteSurface(surfaceId);
+ removeSurfaceId(surfaceId);
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[useA2UISurface] 删除 Surface:', surfaceId);
+ }
+ return;
+ }
+
+ const hasCreate = surfaceMessages.some((msg) => msg.createSurface);
+ const existed = surfaceStateManager.hasSurface(surfaceId);
+
+ // 创建型 / 首次出现 → 整批转换并注册
+ if (hasCreate || !existed) {
+ const schema = convertA2UIMessagesToJsonRender(surfaceMessages);
+ if (schema) {
+ const catalogId = surfaceMessages.find((m) => m.createSurface)?.createSurface?.catalogId;
+ surfaceStateManager.registerSurface(surfaceId, schema, catalogId);
+ addSurfaceId(surfaceId);
+ if (debug) {
+ // eslint-disable-next-line no-console
+ console.log('[useA2UISurface] 注册 Surface:', surfaceId);
+ }
+ // 同批内已经包含 updateDataModel 的初始数据,convertA2UIMessagesToJsonRender 已处理
+ // 不需要再次走 updateData 路径
+ return;
+ }
+ }
+
+ // 已存在的 Surface:分别派发各类消息
+ let mergedSchema: JsonRenderSchema | null = surfaceStateManager.getSchema(surfaceId);
+ let schemaDirty = false;
+
+ for (const msg of surfaceMessages) {
+ if (msg.updateComponents && mergedSchema) {
+ mergedSchema = applyA2UIUpdates(mergedSchema, msg.updateComponents.components as any[]);
+ schemaDirty = true;
+ } else if (msg.updateDataModel) {
+ // updateDataModel 走 surfaceStateManager 标准订阅路径
+ const { path, op, value } = msg.updateDataModel;
+ surfaceStateManager.updateData(surfaceId, path, op || 'replace', value);
+ }
+ }
+
+ // 组件树变化:通过 updateSchema 通知订阅者
+ if (schemaDirty && mergedSchema) {
+ surfaceStateManager.updateSchema(surfaceId, mergedSchema);
+ addSurfaceId(surfaceId);
+ }
+ });
+ },
+ [addSurfaceId, removeSurfaceId, debug],
+ );
+
+ const clearAllSurfaces = useCallback(() => {
+ // 仅清除本 hook 创建的 surface,避免影响其他模块
+ surfaceIdsRef.current.forEach((id) => surfaceStateManager.deleteSurface(id));
+ surfaceIdsRef.current = [];
+ notify();
+ }, [notify]);
+
+ const hasSurface = useCallback((surfaceId: string) => surfaceStateManager.hasSurface(surfaceId), []);
+
+ return useMemo(
+ () => ({
+ surfaceIds: snapshot.ids,
+ processMessages,
+ clearAllSurfaces,
+ hasSurface,
+ }),
+ [snapshot, processMessages, clearAllSurfaces, hasSurface],
+ );
+}
+
+/* ------------------------------------------------------------------ */
+/* A2UI Surface Renderer */
+/* ------------------------------------------------------------------ */
+
+export interface A2UISurfaceRendererProps {
+ /** Surface ID */
+ surfaceId: string;
+ /** 组件注册表(必传) */
+ registry: ComponentRegistry;
+ /** Action 处理器映射,与 JsonRenderActivityRenderer.actionHandlers 协议一致 */
+ actionHandlers?: Record) => void | Promise>;
+}
+
+/**
+ * 渲染指定 Surface 的 UI
+ * 内部订阅 surfaceStateManager 状态变化,自动响应 schema/data 更新
+ *
+ * 渲染委托给 JsonRenderActivityRenderer,复用其 DataProvider/VisibilityProvider/ActionProvider 链路
+ */
+export const A2UISurfaceRenderer: React.FC = ({ surfaceId, registry, actionHandlers }) => {
+ // 订阅指定 surface 的 schema 变化
+ const subscribe = useCallback(
+ (listener: () => void) => surfaceStateManager.subscribe(surfaceId, listener),
+ [surfaceId],
+ );
+
+ // 缓存最近一次 schema 引用,确保 getSnapshot 引用稳定(避免 useSyncExternalStore 抖动)
+ const lastSchemaRef = useRef(null);
+ const getSnapshot = useCallback(() => {
+ const next = surfaceStateManager.getSchema(surfaceId);
+ if (next !== lastSchemaRef.current) {
+ lastSchemaRef.current = next;
+ }
+ return lastSchemaRef.current;
+ }, [surfaceId]);
+
+ const schema = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
+
+ if (!schema) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export default A2UISurfaceRenderer;
+
+/* ------------------------------------------------------------------ */
+/* Re-export 给消费方使用的工具 */
+/* ------------------------------------------------------------------ */
+
+export { applyA2UIDataUpdate, applyA2UIUpdates, convertA2UIMessagesToJsonRender };
+export type { A2UIMessage, JsonRenderSchema };
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/renderer/JsonRenderActivityRenderer.tsx b/packages/pro-components/chat/chat-engine/components/json-render/renderer/JsonRenderActivityRenderer.tsx
new file mode 100644
index 0000000000..7dada1d1e8
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/renderer/JsonRenderActivityRenderer.tsx
@@ -0,0 +1,104 @@
+/**
+ * json-render Activity 渲染器
+ * 基于 TDesign ChatEngine 的 Activity 机制集成 json-render
+ *
+ * 核心特性:
+ * 1. 支持 ACTIVITY_SNAPSHOT 全量渲染
+ * 2. 支持 ACTIVITY_DELTA 增量更新(Delta Merge 由数据层完成,此处接收完整 Schema)
+ * 3. 使用 React.memo + react-fast-compare 优化渲染性能
+ *
+ */
+
+import React, { useMemo } from 'react';
+import isEqual from 'react-fast-compare';
+import { JsonRenderElement } from './JsonUIRenderer';
+import { DataProvider, ActionProvider, VisibilityProvider } from '../contexts';
+import type { JsonRenderActivityProps, ComponentRegistry } from '../types';
+
+export interface JsonRenderActivityRendererProps extends JsonRenderActivityProps {
+ /** 组件注册表(必须) */
+ registry: ComponentRegistry;
+ /**
+ * Action 处理器映射表
+ *
+ * 示例:
+ * ```tsx
+ * const actionHandlers = {
+ * submit: async (params) => { ... },
+ * reset: async (params) => { ... },
+ * cancel: async (params) => { ... },
+ * };
+ * ```
+ */
+ actionHandlers?: Record) => void | Promise>;
+ /** 显示调试信息 */
+ debug?: boolean;
+}
+
+/**
+ * json-render Activity 渲染器组件
+ */
+const JsonRenderActivityRendererInner: React.FC = ({
+ activityType,
+ content,
+ messageId,
+ registry,
+ actionHandlers = {},
+}) => {
+ // 直接在渲染阶段做校验
+ const isValidSchema = content && content.root && content.elements && content.elements[content.root];
+
+ // 数据处理:使用 useMemo 缓存,只在 content.data 变化时重新计算
+ const renderData = useMemo(() => content?.data || {}, [content?.data]);
+
+ // todo: Schema 无效时显示加载状态
+ // if (!isValidSchema) {
+ // return (
+ //
+ // 数据初始化中...
+ //
+ // );
+ // }
+ return (
+
+ );
+};
+
+/**
+ * 使用 React.memo 包装,配合 react-fast-compare 进行高效的深比较
+ *
+ * 对比策略:
+ * 1. registry 引用比较(通常是稳定的)
+ * 2. actionHandlers 引用比较(建议使用 useMemo 稳定化)
+ * 3. content 使用 react-fast-compare 深比较(比 JSON.stringify 快 3-5 倍)
+ */
+export const JsonRenderActivityRenderer = React.memo(
+ JsonRenderActivityRendererInner,
+ (prevProps, nextProps) => {
+ // registry 变化必须重渲染
+ if (prevProps.registry !== nextProps.registry) return false;
+
+ // actionHandlers 变化必须重渲染
+ if (prevProps.actionHandlers !== nextProps.actionHandlers) return false;
+
+ // content 引用相同,跳过渲染
+ if (prevProps.content === nextProps.content) return true;
+
+ // 使用 react-fast-compare 进行高效深比较
+ // 比 JSON.stringify 快 3-5 倍,且能正确处理循环引用
+ return isEqual(prevProps.content, nextProps.content);
+ },
+);
+
+/**
+ * 默认导出
+ */
+export default JsonRenderActivityRenderer;
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/renderer/JsonUIRenderer.tsx b/packages/pro-components/chat/chat-engine/components/json-render/renderer/JsonUIRenderer.tsx
new file mode 100644
index 0000000000..b5be6e5405
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/renderer/JsonUIRenderer.tsx
@@ -0,0 +1,180 @@
+'use client';
+
+import React, { useLayoutEffect, useMemo, useRef } from 'react';
+
+import {
+ ActionProvider,
+ ConfirmDialog,
+ DataProvider,
+ useActions,
+ useIsVisible,
+ ValidationProvider,
+ VisibilityProvider,
+} from '../contexts';
+import { RenderContext, TreeStore, useElement, useRenderContext, useRoot } from '../contexts/tree';
+
+import type { ComponentType, ReactNode } from 'react';
+import type { Catalog } from '@json-render/core';
+import type { ComponentRegistry, RendererProps } from '../types';
+
+/**
+ * ElementRenderer - 使用 selector 模式订阅特定 element
+ *
+ * 性能优化原理:
+ * 1. 上游使用 Structural Sharing,未修改的节点保持原引用
+ * 2. useSyncExternalStore 的 getSnapshot 返回 element 引用
+ * 3. 引用相同 → 不重渲染;引用不同 → 重渲染
+ */
+const ElementRenderer = React.memo(function ElementRenderer({ elementKey }: { elementKey: string }) {
+ const { registry, loading, fallback } = useRenderContext();
+ const element = useElement(elementKey);
+ const isVisible = useIsVisible(element?.visible);
+ const { execute } = useActions();
+
+ // Don't render if element doesn't exist or not visible
+ if (!element || !isVisible) {
+ return null;
+ }
+
+ // Get the component renderer
+ const Component = registry[element.type] ?? fallback;
+
+ if (!Component) {
+ return null;
+ }
+
+ // 子组件独立订阅
+ const children = element.children?.map((childKey) => );
+
+ return (
+
+ {children}
+
+ );
+});
+
+/**
+ * Root renderer - 订阅 root 变化
+ */
+function RootRenderer() {
+ const root = useRoot();
+
+ if (!root) {
+ return null;
+ }
+
+ return ;
+}
+
+/**
+ * Main renderer component
+ *
+ * 架构说明:
+ * - JsonRenderElement 作为入口,不因 tree 变化而重渲染
+ * - tree 更新通过 store.setTree() 通知订阅者
+ * - 每个 ElementRenderer 独立订阅自己的 element
+ * - 配合上游 Structural Sharing,只有真正变化的节点才重渲染
+ */
+export function JsonRenderElement({ tree, registry, loading, fallback }: RendererProps) {
+ // 创建稳定的 store 引用
+ const storeRef = useRef(null);
+ if (!storeRef.current) {
+ storeRef.current = new TreeStore();
+ }
+ const store = storeRef.current;
+
+ // 确保子组件在 useEffect 或布局计算前能获取到最新的 tree。
+ useLayoutEffect(() => {
+ store.setTree(tree?.root ? tree : null);
+ }, [tree, store]);
+
+ const contextValue = useMemo(
+ () => ({
+ store,
+ registry,
+ loading,
+ fallback,
+ }),
+ [store, registry, loading, fallback],
+ );
+
+ // 边界保护
+ if (!tree?.root) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
+
+/**
+ * Props for JSONUIProvider
+ */
+export interface JSONUIProviderProps {
+ /** Component registry */
+ registry: ComponentRegistry;
+ /** Initial data model */
+ initialData?: Record;
+ /** Auth state */
+ authState?: { isSignedIn: boolean; user?: Record };
+ /** Action handlers */
+ actionHandlers?: Record) => Promise | unknown>;
+ /** Navigation function */
+ navigate?: (path: string) => void;
+ /** Custom validation functions */
+ validationFunctions?: Record) => boolean>;
+ /** Callback when data changes */
+ onDataChange?: (path: string, value: unknown) => void;
+ children: ReactNode;
+}
+
+export function JSONUIProvider({
+ initialData,
+ authState,
+ actionHandlers,
+ navigate,
+ validationFunctions,
+ onDataChange,
+ children,
+}: JSONUIProviderProps) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+}
+
+/**
+ * Renders the confirmation dialog when needed
+ */
+function ConfirmationDialogManager() {
+ const { pendingConfirmation, confirm, cancel } = useActions();
+
+ if (!pendingConfirmation?.action.confirm) {
+ return null;
+ }
+
+ return ;
+}
+
+/**
+ * Helper to create a renderer component from a catalog
+ */
+export function createRendererFromCatalog(
+ _catalog: C,
+ registry: ComponentRegistry,
+): ComponentType> {
+ return function CatalogRenderer(props: Omit) {
+ return ;
+ };
+}
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/renderer/_index.tsx b/packages/pro-components/chat/chat-engine/components/json-render/renderer/_index.tsx
new file mode 100644
index 0000000000..df584c51bd
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/renderer/_index.tsx
@@ -0,0 +1,201 @@
+'use client';
+
+import React from 'react';
+
+import {
+ ActionProvider,
+ ConfirmDialog,
+ DataProvider,
+ useActions,
+ useIsVisible,
+ ValidationProvider,
+ VisibilityProvider,
+} from '../contexts';
+
+import type { ComponentType, ReactNode } from 'react';
+import type { ActionBinding, Catalog, Spec, UIElement } from '@json-render/core';
+
+/**
+ * Props passed to component renderers
+ */
+export interface ComponentRenderProps> {
+ /** The element being rendered */
+ element: UIElement;
+ /** Rendered children */
+ children?: ReactNode;
+ /** Execute an action */
+ onAction?: (action: ActionBinding) => void;
+ /** Whether the parent is loading */
+ loading?: boolean;
+}
+
+/**
+ * Component renderer type
+ */
+export type ComponentRenderer> = ComponentType>;
+
+/**
+ * Registry of component renderers
+ */
+export type ComponentRegistry = Record>;
+
+/**
+ * Props for the Renderer component
+ */
+export interface RendererProps {
+ /** The UI tree to render */
+ tree: Spec | null;
+ /** Component registry */
+ registry: ComponentRegistry;
+ /** Whether the tree is currently loading/streaming */
+ loading?: boolean;
+ /** Fallback component for unknown types */
+ fallback?: ComponentRenderer;
+}
+
+/**
+ * Element renderer component
+ */
+function ElementRenderer({
+ element,
+ tree,
+ registry,
+ loading,
+ fallback,
+}: {
+ element: UIElement;
+ tree: Spec;
+ registry: ComponentRegistry;
+ loading?: boolean;
+ fallback?: ComponentRenderer;
+}) {
+ const isVisible = useIsVisible(element.visible);
+ const { execute } = useActions();
+
+ // Don't render if not visible
+ if (!isVisible) {
+ return null;
+ }
+
+ // Get the component renderer
+ const Component = registry[element.type] ?? fallback;
+
+ if (!Component) {
+ console.warn(`No renderer for component type: ${element.type}`);
+ return null;
+ }
+
+ // Render children
+ const children = element.children?.map((childKey) => {
+ const childElement = tree.elements[childKey];
+ if (!childElement) {
+ return null;
+ }
+ return (
+
+ );
+ });
+
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * Main renderer component
+ */
+export function JsonRenderElement({ tree, registry, loading, fallback }: RendererProps) {
+ if (!tree || !tree.root) {
+ return null;
+ }
+
+ const rootElement = tree.elements[tree.root];
+ if (!rootElement) {
+ return null;
+ }
+
+ return (
+
+ );
+}
+
+/**
+ * Props for JSONUIProvider
+ */
+export interface JSONUIProviderProps {
+ /** Component registry */
+ registry: ComponentRegistry;
+ /** Initial data model */
+ initialData?: Record;
+ /** Auth state */
+ authState?: { isSignedIn: boolean; user?: Record };
+ /** Action handlers */
+ actionHandlers?: Record) => Promise | unknown>;
+ /** Navigation function */
+ navigate?: (path: string) => void;
+ /** Custom validation functions */
+ validationFunctions?: Record) => boolean>;
+ /** Callback when data changes */
+ onDataChange?: (path: string, value: unknown) => void;
+ children: ReactNode;
+}
+
+/**
+ * Combined provider for all JSONUI contexts
+ */
+export function JSONUIProvider({
+ initialData,
+ authState,
+ actionHandlers,
+ navigate,
+ validationFunctions,
+ onDataChange,
+ children,
+}: JSONUIProviderProps) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+}
+
+/**
+ * Renders the confirmation dialog when needed
+ */
+function ConfirmationDialogManager() {
+ const { pendingConfirmation, confirm, cancel } = useActions();
+
+ if (!pendingConfirmation?.action.confirm) {
+ return null;
+ }
+
+ return ;
+}
+
+/**
+ * Helper to create a renderer component from a catalog
+ */
+export function createRendererFromCatalog(
+ _catalog: C,
+ registry: ComponentRegistry,
+): ComponentType> {
+ return function CatalogRenderer(props: Omit) {
+ return ;
+ };
+}
diff --git a/packages/pro-components/chat/chat-engine/components/json-render/types.ts b/packages/pro-components/chat/chat-engine/components/json-render/types.ts
new file mode 100644
index 0000000000..aa1e192698
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/json-render/types.ts
@@ -0,0 +1,104 @@
+/**
+ * json-render 集成相关类型定义
+ */
+
+import type { ComponentType, ReactNode } from 'react';
+import type { ActionBinding, Spec, UIElement } from '@json-render/core';
+import type { JsonRenderSchema } from '@tdesign/ai-chat-engine';
+
+/**
+ * Props passed to component renderers
+ */
+export interface ComponentRenderProps> {
+ /** The element being rendered */
+ element: UIElement;
+ /** Rendered children */
+ children?: ReactNode;
+ /** Execute an action */
+ onAction?: (action: ActionBinding) => void;
+ /** Whether the parent is loading */
+ loading?: boolean;
+}
+
+/**
+ * Component renderer type
+ */
+export type ComponentRenderer> = ComponentType>;
+
+/**
+ * Registry of component renderers
+ */
+export type ComponentRegistry = Record>;
+
+/**
+ * Props for the Renderer component
+ */
+export interface RendererProps {
+ /** The UI tree to render */
+ tree: Spec | null;
+ /** Component registry */
+ registry: ComponentRegistry;
+ /** Whether the tree is currently loading/streaming */
+ loading?: boolean;
+ /** Fallback component for unknown types */
+ fallback?: ComponentRenderer;
+}
+
+/**
+ * json-render Activity 内容格式
+ * 用于 ACTIVITY_SNAPSHOT 和 ACTIVITY_DELTA 事件
+ */
+// export interface JsonRenderSchema extends Spec {
+// // json-render 标准 Spec 结构
+// root: string;
+// elements: Record;
+// // 可选的数据模型
+// data?: Record;
+// }
+
+/**
+ * 组件目录(Catalog)定义
+ * 映射组件类型到 React 组件实现
+ * 使用 @json-render/react 的 ComponentRegistry 类型
+ */
+export type ComponentCatalog = ComponentRegistry;
+
+/**
+ * 渲染上下文配置
+ */
+export interface JsonRenderContext {
+ /** 渲染模式:direct(直接模式) | adapter(适配模式) */
+ mode: 'direct' | 'adapter';
+ /** 组件目录 */
+ catalog: ComponentCatalog;
+ /** 可选:自定义数据 */
+ customData?: Record;
+}
+
+/**
+ * 增量更新信息
+ * 从 event-mapper 的 deltaInfo 传递过来
+ */
+export interface DeltaInfo {
+ /** 新增元素的起始索引 */
+ fromIndex: number;
+ /** 新增元素的结束索引 */
+ toIndex: number;
+}
+
+/**
+ * json-render Activity 渲染器 Props
+ */
+export interface JsonRenderActivityProps {
+ /** Activity 类型 */
+ activityType: string;
+ /** json-render Schema 内容 */
+ content: JsonRenderSchema;
+ /** 关联的消息 ID */
+ messageId: string;
+ /** 扩展属性(包含 deltaInfo) */
+ ext?: {
+ deltaInfo?: DeltaInfo;
+ [key: string]: any;
+ };
+}
diff --git a/packages/pro-components/chat/chat-engine/components/shared/ComponentErrorBoundary.tsx b/packages/pro-components/chat/chat-engine/components/shared/ComponentErrorBoundary.tsx
new file mode 100644
index 0000000000..6270ae970f
--- /dev/null
+++ b/packages/pro-components/chat/chat-engine/components/shared/ComponentErrorBoundary.tsx
@@ -0,0 +1,44 @@
+import React, { Component, ErrorInfo } from 'react';
+
+interface ErrorBoundaryProps {
+ children: React.ReactNode;
+ /** 组件标识,用于错误日志 */
+ componentName: string;
+ /** 日志前缀,如 'ActivityRenderer' 或 'ToolCallRenderer' */
+ logPrefix?: string;
+ /** 自定义错误渲染,默认返回 null */
+ fallback?: React.ReactNode;
+}
+
+interface ErrorBoundaryState {
+ hasError: boolean;
+ error: Error | null;
+}
+
+/**
+ * 通用错误边界组件
+ * 捕获子组件渲染错误,防止整个对话列表崩溃
+ */
+export class ComponentErrorBoundary extends Component