@alicloud/appflow-chat 0.0.4 → 0.0.5-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/appflow-chat.cjs.js +857 -164
- package/dist/appflow-chat.esm.js +19844 -13557
- package/dist/types/index.d.ts +243 -2
- package/package.json +3 -1
- package/src/components/A2UIRenderer/A2UIRenderer.tsx +181 -0
- package/src/components/A2UIRenderer/index.ts +1 -0
- package/src/components/HumanVerify/CustomParamsRenderer/ArrayField.tsx +6 -4
- package/src/components/HumanVerify/CustomParamsRenderer/EnumField.tsx +5 -3
- package/src/components/HumanVerify/CustomParamsRenderer/FieldRenderer.tsx +6 -4
- package/src/components/HumanVerify/CustomParamsRenderer/FileField.tsx +38 -30
- package/src/components/HumanVerify/CustomParamsRenderer/ObjectField.tsx +4 -2
- package/src/components/HumanVerify/CustomParamsRenderer/TimeField.tsx +3 -1
- package/src/components/HumanVerify/HistoryCard.tsx +6 -4
- package/src/components/HumanVerify/HumanVerify.tsx +5 -3
- package/src/components/MessageBubble.tsx +26 -4
- package/src/components/RichMessageBubble.tsx +4 -2
- package/src/core/RichBubbleContent.tsx +4 -2
- package/src/core/SourceContent.tsx +5 -3
- package/src/core/WebSearchContent.tsx +3 -1
- package/src/i18n/LocaleContext.tsx +70 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/translate.ts +42 -0
- package/src/i18n/useTranslation.ts +39 -0
- package/src/i18n/utils.ts +67 -0
- package/src/index.ts +31 -0
- package/src/locales/en-US.ts +77 -0
- package/src/locales/index.ts +7 -0
- package/src/locales/types.ts +35 -0
- package/src/locales/zh-CN.ts +78 -0
- package/src/markdown/components/Chart.tsx +3 -1
- package/src/markdown/components/Mermaid.tsx +8 -6
- package/src/markdown/index.tsx +11 -8
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
|
+
import { A2UIViewerProps } from '@a2ui/react';
|
|
2
|
+
import { ComponentRegistry } from '@a2ui/react';
|
|
1
3
|
import { default as default_2 } from 'react';
|
|
4
|
+
import { OnActionCallback } from '@a2ui/react';
|
|
2
5
|
import { Provider } from 'react';
|
|
6
|
+
import { ServerToClientMessage } from '@a2ui/react';
|
|
7
|
+
|
|
8
|
+
/** A2UI 协议消息(v0.8 格式,透传给 @a2ui/react 处理) */
|
|
9
|
+
export declare type A2UIMessage = ServerToClientMessage;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A2UIStaticViewer - A2UI 静态 JSON 渲染组件
|
|
13
|
+
*
|
|
14
|
+
* 用于直接从静态的组件定义和数据渲染 UI,无需流式消息。
|
|
15
|
+
* 适用于已有完整 A2UI 组件树的场景。
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const components = [
|
|
20
|
+
* { id: 'root', component: { Card: { child: 'text' } } },
|
|
21
|
+
* { id: 'text', component: { Text: { text: { path: '/message' } } } },
|
|
22
|
+
* ];
|
|
23
|
+
*
|
|
24
|
+
* <A2UIStaticViewer
|
|
25
|
+
* root="root"
|
|
26
|
+
* components={components}
|
|
27
|
+
* data={{ message: 'Hello World!' }}
|
|
28
|
+
* onAction={(action) => console.log('Action:', action)}
|
|
29
|
+
* />
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare const A2UIStaticViewer: default_2.FC<A2UIStaticViewerProps>;
|
|
33
|
+
|
|
34
|
+
/** A2UIViewer 的 Props,用于静态 JSON 渲染场景 */
|
|
35
|
+
export declare type A2UIStaticViewerProps = A2UIViewerProps;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A2UISurface - A2UI 声明式 UI 渲染组件(流式消息场景)
|
|
39
|
+
*
|
|
40
|
+
* 接收 Agent 发送的 A2UI 协议消息数组,增量处理并渲染对应的 Surface。
|
|
41
|
+
* 内部封装了 A2UIProvider,可直接作为 BubbleContent 的 children 使用。
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* // 作为 BubbleContent 的 children 使用
|
|
46
|
+
* <BubbleContent content={content} status={status}>
|
|
47
|
+
* <A2UISurface
|
|
48
|
+
* messages={a2uiMessages}
|
|
49
|
+
* surfaceId="main"
|
|
50
|
+
* onAction={(msg) => console.log('用户操作:', msg)}
|
|
51
|
+
* />
|
|
52
|
+
* </BubbleContent>
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare const A2UISurface: default_2.FC<A2UISurfaceProps>;
|
|
56
|
+
|
|
57
|
+
export declare interface A2UISurfaceProps {
|
|
58
|
+
/** A2UI JSON 消息数组(来自 Agent 的响应) */
|
|
59
|
+
messages: A2UIMessage[];
|
|
60
|
+
/** 渲染的 Surface ID(默认 'main') */
|
|
61
|
+
surfaceId?: string;
|
|
62
|
+
/** 用户在 A2UI 组件上的交互回调 */
|
|
63
|
+
onAction?: OnActionCallback;
|
|
64
|
+
/** 自定义组件注册表(可选,默认使用内置组件) */
|
|
65
|
+
registry?: ComponentRegistry;
|
|
66
|
+
/** 自定义类名 */
|
|
67
|
+
className?: string;
|
|
68
|
+
/** 自定义样式 */
|
|
69
|
+
style?: default_2.CSSProperties;
|
|
70
|
+
}
|
|
3
71
|
|
|
4
72
|
/**
|
|
5
73
|
* AssociationPropertyMetadata 类型定义
|
|
@@ -320,6 +388,21 @@ export declare interface CustomParamsRendererProps extends UploadConfig {
|
|
|
320
388
|
errors?: Record<string, string>;
|
|
321
389
|
}
|
|
322
390
|
|
|
391
|
+
/** 递归生成所有叶子节点的扁平路径联合类型 */
|
|
392
|
+
declare type DeepKey<T, P extends string = ''> = {
|
|
393
|
+
[K in keyof T & string]: T[K] extends object ? DeepKey<T[K], `${P}${P extends '' ? '' : '.'}${K}`> : `${P}${P extends '' ? '' : '.'}${K}`;
|
|
394
|
+
}[keyof T & string];
|
|
395
|
+
|
|
396
|
+
/** 深度可选,用于 LocaleProvider 的 overrides 字段 */
|
|
397
|
+
export declare type DeepPartial<T> = {
|
|
398
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
/** 把所有叶子节点的值类型统一为 string,并去掉 readonly */
|
|
402
|
+
declare type DeepStringify<T> = {
|
|
403
|
+
-readonly [K in keyof T]: T[K] extends object ? DeepStringify<T[K]> : string;
|
|
404
|
+
};
|
|
405
|
+
|
|
323
406
|
export declare type DocReferenceItem = SourceItem;
|
|
324
407
|
|
|
325
408
|
/**
|
|
@@ -357,6 +440,8 @@ export declare interface DocReferencesProps {
|
|
|
357
440
|
|
|
358
441
|
declare type EnumDisplayStyle = 'select' | 'checkbox' | 'radio' | 'multi-select';
|
|
359
442
|
|
|
443
|
+
export declare const enUS: Locale;
|
|
444
|
+
|
|
360
445
|
declare type ExtendedParamType = 'time' | 'file';
|
|
361
446
|
|
|
362
447
|
declare type FileSubType = 'default' | 'jpg' | 'png' | 'svg' | 'doc' | 'ppt' | 'excel' | 'txt' | 'markdown' | 'zip';
|
|
@@ -367,6 +452,12 @@ declare type FileSubType = 'default' | 'jpg' | 'png' | 'svg' | 'doc' | 'ppt' | '
|
|
|
367
452
|
*/
|
|
368
453
|
declare type FileUploader = (file: Blob, uploadUrl: string) => Promise<void>;
|
|
369
454
|
|
|
455
|
+
/** 获取当前全局 locale */
|
|
456
|
+
export declare function getGlobalLocale(): Locale;
|
|
457
|
+
|
|
458
|
+
/** 获取当前全局 locale 名称 */
|
|
459
|
+
export declare function getGlobalLocaleName(): string;
|
|
460
|
+
|
|
370
461
|
/**
|
|
371
462
|
* HistoryCard 历史卡片组件 (SDK 版本)
|
|
372
463
|
* 用于展示历史对话中的 card 类型消息(只读模式)
|
|
@@ -448,6 +539,40 @@ export declare const loadEchartsScript: () => Promise<void>;
|
|
|
448
539
|
|
|
449
540
|
export declare const loadMermaidScript: () => Promise<void>;
|
|
450
541
|
|
|
542
|
+
/** 词条对象类型:保留 zhCN 的结构,但叶子节点统一为 string */
|
|
543
|
+
export declare type Locale = DeepStringify<typeof zhCN>;
|
|
544
|
+
|
|
545
|
+
export declare const LocaleContext: default_2.Context<LocaleContextValue>;
|
|
546
|
+
|
|
547
|
+
export declare interface LocaleContextValue {
|
|
548
|
+
locale: Locale;
|
|
549
|
+
localeName: string;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* LocaleProvider
|
|
554
|
+
*
|
|
555
|
+
* 用法示例:
|
|
556
|
+
* ```tsx
|
|
557
|
+
* import { LocaleProvider, enUS } from '@alicloud/appflow-chat';
|
|
558
|
+
*
|
|
559
|
+
* <LocaleProvider locale={enUS} localeName="en-US">
|
|
560
|
+
* <App />
|
|
561
|
+
* </LocaleProvider>
|
|
562
|
+
* ```
|
|
563
|
+
*/
|
|
564
|
+
export declare const LocaleProvider: default_2.FC<LocaleProviderProps>;
|
|
565
|
+
|
|
566
|
+
export declare interface LocaleProviderProps {
|
|
567
|
+
/** 完整 locale 对象,默认使用内置 zhCN */
|
|
568
|
+
locale?: Locale;
|
|
569
|
+
/** 语言标识,如 'zh-CN' / 'en-US' */
|
|
570
|
+
localeName?: string;
|
|
571
|
+
/** 部分覆盖:在选定 locale 基础上 deep merge 自定义文案 */
|
|
572
|
+
overrides?: DeepPartial<Locale>;
|
|
573
|
+
children: default_2.ReactNode;
|
|
574
|
+
}
|
|
575
|
+
|
|
451
576
|
/**
|
|
452
577
|
* MarkdownRenderer - Markdown渲染组件
|
|
453
578
|
*
|
|
@@ -530,14 +655,18 @@ export declare interface MessageBubbleProps {
|
|
|
530
655
|
onReferenceClick?: (item: DocReferenceItem) => void;
|
|
531
656
|
/** 点击网页搜索结果回调(不传则使用默认实现) */
|
|
532
657
|
onWebSearchClick?: (items: DocReferenceItem[]) => void;
|
|
533
|
-
/** 事件类型(用于特殊消息如 humanVerify、historyCard) */
|
|
534
|
-
eventType?: 'humanVerify' | 'historyCard';
|
|
658
|
+
/** 事件类型(用于特殊消息如 humanVerify、historyCard、a2ui) */
|
|
659
|
+
eventType?: 'humanVerify' | 'historyCard' | 'a2ui';
|
|
535
660
|
/** HumanVerify 相关数据 */
|
|
536
661
|
humanVerifyData?: HumanVerifyData;
|
|
537
662
|
/** HistoryCard 相关数据(历史对话中的审核卡片) */
|
|
538
663
|
historyCardData?: HistoryCardData;
|
|
539
664
|
/** HumanVerify 提交回调 */
|
|
540
665
|
onHumanVerifySubmit?: (data: HumanVerifySubmitData) => void;
|
|
666
|
+
/** A2UI 消息数组(Agent 生成的声明式 UI 描述) */
|
|
667
|
+
a2uiMessages?: A2UIMessage[];
|
|
668
|
+
/** A2UI 用户交互回调 */
|
|
669
|
+
onA2UIAction?: (action: any) => void;
|
|
541
670
|
}
|
|
542
671
|
|
|
543
672
|
export declare interface ModelCapabilities {
|
|
@@ -672,6 +801,9 @@ export declare interface RichMessageBubbleProps {
|
|
|
672
801
|
onWebSearchClick?: (items: DocReferenceItem[]) => void;
|
|
673
802
|
}
|
|
674
803
|
|
|
804
|
+
/** 设置全局 locale,由 LocaleProvider 在挂载/更新时调用 */
|
|
805
|
+
export declare function setGlobalLocale(locale: Locale, localeName?: string): void;
|
|
806
|
+
|
|
675
807
|
/**
|
|
676
808
|
* ChatService - 独立的聊天服务类
|
|
677
809
|
* 用于自定义UI场景,不依赖React
|
|
@@ -744,6 +876,21 @@ export declare interface SourceItem {
|
|
|
744
876
|
|
|
745
877
|
declare type TimeSubType = 'year-month' | 'year-month-day' | 'datetime';
|
|
746
878
|
|
|
879
|
+
/**
|
|
880
|
+
* 全局翻译函数(非 Hook 版本)
|
|
881
|
+
*
|
|
882
|
+
* @example
|
|
883
|
+
* translate('common.loading') // '加载中...'
|
|
884
|
+
* translate('humanVerify.placeholder.input', { title: '名称' }) // '请输入名称'
|
|
885
|
+
*/
|
|
886
|
+
export declare function translate(key: TranslationKey, params?: TranslationParams): string;
|
|
887
|
+
|
|
888
|
+
/** 所有合法的扁平 key 路径,如 'common.loading' | 'humanVerify.placeholder.input' */
|
|
889
|
+
export declare type TranslationKey = DeepKey<typeof zhCN>;
|
|
890
|
+
|
|
891
|
+
/** 插值参数 */
|
|
892
|
+
export declare type TranslationParams = Record<string, string | number>;
|
|
893
|
+
|
|
747
894
|
/**
|
|
748
895
|
* 上传配置
|
|
749
896
|
*/
|
|
@@ -787,6 +934,24 @@ export declare const useCustomParamsRenderer: (schema: CustomParamSchema) => {
|
|
|
787
934
|
|
|
788
935
|
export declare const useRichBubbleContext: () => RichBubbleContextValue;
|
|
789
936
|
|
|
937
|
+
/**
|
|
938
|
+
* 在 React 组件内消费国际化文案
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* const { t } = useTranslation();
|
|
942
|
+
* <Input placeholder={t('humanVerify.placeholder.input', { title: '名称' })} />
|
|
943
|
+
*/
|
|
944
|
+
export declare function useTranslation(): UseTranslationResult;
|
|
945
|
+
|
|
946
|
+
export declare interface UseTranslationResult {
|
|
947
|
+
/** 翻译函数 */
|
|
948
|
+
t: (key: TranslationKey, params?: TranslationParams) => string;
|
|
949
|
+
/** 当前 locale 对象 */
|
|
950
|
+
locale: Locale;
|
|
951
|
+
/** 当前语言标识 */
|
|
952
|
+
localeName: string;
|
|
953
|
+
}
|
|
954
|
+
|
|
790
955
|
/**
|
|
791
956
|
* 校验 CustomParams 的值
|
|
792
957
|
* @param schema Schema 定义
|
|
@@ -894,4 +1059,80 @@ export declare interface WebSearchPanelProps {
|
|
|
894
1059
|
style?: default_2.CSSProperties;
|
|
895
1060
|
}
|
|
896
1061
|
|
|
1062
|
+
/**
|
|
1063
|
+
* 中文词条(默认语言)
|
|
1064
|
+
*
|
|
1065
|
+
* 该文件作为 Locale 类型的基准结构,所有其他语言文件必须实现相同的 key 结构。
|
|
1066
|
+
* 新增文案时优先在此处添加,再补齐其他语言文件。
|
|
1067
|
+
*/
|
|
1068
|
+
export declare const zhCN: {
|
|
1069
|
+
readonly common: {
|
|
1070
|
+
readonly loading: "加载中...";
|
|
1071
|
+
readonly confirm: "确定";
|
|
1072
|
+
readonly cancel: "取消";
|
|
1073
|
+
readonly submit: "提交";
|
|
1074
|
+
readonly retry: "重试";
|
|
1075
|
+
readonly copy: "复制";
|
|
1076
|
+
readonly copied: "已复制";
|
|
1077
|
+
readonly placeholderSelect: "请选择";
|
|
1078
|
+
};
|
|
1079
|
+
readonly humanVerify: {
|
|
1080
|
+
readonly requiredAll: "请填写所有必填项";
|
|
1081
|
+
readonly submitted: "已提交";
|
|
1082
|
+
readonly pending: "待提交";
|
|
1083
|
+
readonly placeholder: {
|
|
1084
|
+
readonly input: "请输入{title}";
|
|
1085
|
+
readonly select: "请选择";
|
|
1086
|
+
};
|
|
1087
|
+
readonly file: {
|
|
1088
|
+
readonly supportAll: "支持所有文件格式";
|
|
1089
|
+
readonly supportFormats: "支持 {formats} 格式";
|
|
1090
|
+
readonly uploadButton: "选择文件";
|
|
1091
|
+
readonly uploading: "上传中";
|
|
1092
|
+
readonly upload: "上传";
|
|
1093
|
+
readonly defaultFileName: "文件";
|
|
1094
|
+
readonly maxSizeError: "文件大小不能超过 {size}";
|
|
1095
|
+
readonly uploadFailed: "文件上传失败";
|
|
1096
|
+
readonly tokenFailed: "获取上传凭证失败";
|
|
1097
|
+
readonly uploaderNotConfigured: "上传功能未配置";
|
|
1098
|
+
readonly uploadMethodNotConfigured: "文件上传方法未配置";
|
|
1099
|
+
readonly getFileIdFailed: "获取文件ID失败";
|
|
1100
|
+
};
|
|
1101
|
+
};
|
|
1102
|
+
readonly webSearch: {
|
|
1103
|
+
readonly title: "搜索结果";
|
|
1104
|
+
readonly foundPages: "已搜索到{count}个网页";
|
|
1105
|
+
};
|
|
1106
|
+
readonly source: {
|
|
1107
|
+
readonly title: "参考资料";
|
|
1108
|
+
readonly answerFrom: "回答来源:";
|
|
1109
|
+
readonly imageFrom: "图片来源:";
|
|
1110
|
+
};
|
|
1111
|
+
readonly rich: {
|
|
1112
|
+
readonly stepLabel: "步骤{index}:";
|
|
1113
|
+
readonly emptyContent: "暂无内容";
|
|
1114
|
+
};
|
|
1115
|
+
readonly markdown: {
|
|
1116
|
+
readonly copyCode: "复制代码";
|
|
1117
|
+
readonly copied: "已复制!";
|
|
1118
|
+
readonly copy: "复制";
|
|
1119
|
+
readonly copiedShort: "已复制";
|
|
1120
|
+
readonly deepThinking: "深度思考";
|
|
1121
|
+
readonly chartLoading: "图表加载中...";
|
|
1122
|
+
readonly tableLoading: "表格加载中...";
|
|
1123
|
+
readonly tableLoadFailed: "表格数据加载失败,请检查数据格式";
|
|
1124
|
+
readonly chartLoadFailed: "图表数据加载失败,请检查数据格式";
|
|
1125
|
+
readonly mermaidLoadFailed: "Mermaid 库加载失败";
|
|
1126
|
+
readonly mermaidNotLoaded: "Mermaid 未正确加载";
|
|
1127
|
+
readonly mermaidRenderFailed: "Mermaid 图表渲染失败";
|
|
1128
|
+
};
|
|
1129
|
+
readonly message: {
|
|
1130
|
+
readonly like: "点赞";
|
|
1131
|
+
readonly dislike: "点踩";
|
|
1132
|
+
readonly regenerate: "重新生成";
|
|
1133
|
+
readonly copy: "复制";
|
|
1134
|
+
readonly copied: "已复制";
|
|
1135
|
+
};
|
|
1136
|
+
};
|
|
1137
|
+
|
|
897
1138
|
export { }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alicloud/appflow-chat",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5-beta.1",
|
|
4
4
|
"description": "Appflow-Chat AI聊天机器人组件库,提供聊天服务和UI组件",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/appflow-chat.cjs.js",
|
|
@@ -58,6 +58,8 @@
|
|
|
58
58
|
"vite-plugin-dts": "^4.3.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
+
"@a2ui/react": "^0.8.0",
|
|
62
|
+
"@a2ui/web_core": "^0.8.0",
|
|
61
63
|
"antd": "^5.0.0",
|
|
62
64
|
"react": "^18.0.0",
|
|
63
65
|
"react-dom": "^18.0.0"
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2UISurface - A2UI 声明式 UI 渲染组件
|
|
3
|
+
*
|
|
4
|
+
* 基于 Google A2UI 协议,将 Agent 生成的声明式 JSON 消息
|
|
5
|
+
* 渲染为原生 React 组件。支持流式增量更新和自定义组件注册。
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React, { useEffect, useRef, useCallback } from 'react';
|
|
10
|
+
import styled from 'styled-components';
|
|
11
|
+
import {
|
|
12
|
+
A2UIProvider,
|
|
13
|
+
A2UIRenderer,
|
|
14
|
+
A2UIViewer,
|
|
15
|
+
useA2UI,
|
|
16
|
+
initializeDefaultCatalog,
|
|
17
|
+
ComponentRegistry,
|
|
18
|
+
} from '@a2ui/react';
|
|
19
|
+
import type {
|
|
20
|
+
ServerToClientMessage,
|
|
21
|
+
OnActionCallback,
|
|
22
|
+
A2UIViewerProps,
|
|
23
|
+
} from '@a2ui/react';
|
|
24
|
+
|
|
25
|
+
// 初始化默认组件目录(全局只需执行一次)
|
|
26
|
+
initializeDefaultCatalog();
|
|
27
|
+
|
|
28
|
+
// ==================== Types ====================
|
|
29
|
+
|
|
30
|
+
/** A2UI 协议消息(v0.8 格式,透传给 @a2ui/react 处理) */
|
|
31
|
+
export type A2UIMessage = ServerToClientMessage;
|
|
32
|
+
|
|
33
|
+
export interface A2UISurfaceProps {
|
|
34
|
+
/** A2UI JSON 消息数组(来自 Agent 的响应) */
|
|
35
|
+
messages: A2UIMessage[];
|
|
36
|
+
/** 渲染的 Surface ID(默认 'main') */
|
|
37
|
+
surfaceId?: string;
|
|
38
|
+
/** 用户在 A2UI 组件上的交互回调 */
|
|
39
|
+
onAction?: OnActionCallback;
|
|
40
|
+
/** 自定义组件注册表(可选,默认使用内置组件) */
|
|
41
|
+
registry?: ComponentRegistry;
|
|
42
|
+
/** 自定义类名 */
|
|
43
|
+
className?: string;
|
|
44
|
+
/** 自定义样式 */
|
|
45
|
+
style?: React.CSSProperties;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A2UIViewer 的 Props,用于静态 JSON 渲染场景 */
|
|
49
|
+
export type A2UIStaticViewerProps = A2UIViewerProps;
|
|
50
|
+
|
|
51
|
+
// ==================== Styled Components ====================
|
|
52
|
+
|
|
53
|
+
const A2UIContainer = styled.div`
|
|
54
|
+
margin-top: 12px;
|
|
55
|
+
width: 100%;
|
|
56
|
+
max-width: 100%;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
// ==================== Internal Component ====================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 内部组件:负责消息处理和 Surface 渲染
|
|
63
|
+
* 必须在 A2UIProvider 内部使用,以获取 useA2UI hook 的上下文
|
|
64
|
+
*/
|
|
65
|
+
const A2UISurfaceInner: React.FC<{
|
|
66
|
+
messages: A2UIMessage[];
|
|
67
|
+
surfaceId: string;
|
|
68
|
+
registry?: ComponentRegistry;
|
|
69
|
+
className?: string;
|
|
70
|
+
style?: React.CSSProperties;
|
|
71
|
+
}> = ({ messages, surfaceId, registry, className, style }) => {
|
|
72
|
+
const { processMessages, getSurface } = useA2UI();
|
|
73
|
+
const processedCountRef = useRef(0);
|
|
74
|
+
|
|
75
|
+
// 增量处理新到达的消息(支持流式场景)
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (messages.length > processedCountRef.current) {
|
|
78
|
+
const newMessages = messages.slice(processedCountRef.current);
|
|
79
|
+
processMessages(newMessages);
|
|
80
|
+
processedCountRef.current = messages.length;
|
|
81
|
+
}
|
|
82
|
+
}, [messages, processMessages]);
|
|
83
|
+
|
|
84
|
+
// 当消息被清空时重置计数器
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (messages.length === 0) {
|
|
87
|
+
processedCountRef.current = 0;
|
|
88
|
+
}
|
|
89
|
+
}, [messages.length]);
|
|
90
|
+
|
|
91
|
+
const surface = getSurface(surfaceId);
|
|
92
|
+
|
|
93
|
+
if (!surface) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<A2UIContainer className={className} style={style}>
|
|
99
|
+
<A2UIRenderer
|
|
100
|
+
surfaceId={surfaceId}
|
|
101
|
+
registry={registry}
|
|
102
|
+
/>
|
|
103
|
+
</A2UIContainer>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// ==================== Exported Components ====================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A2UISurface - A2UI 声明式 UI 渲染组件(流式消息场景)
|
|
111
|
+
*
|
|
112
|
+
* 接收 Agent 发送的 A2UI 协议消息数组,增量处理并渲染对应的 Surface。
|
|
113
|
+
* 内部封装了 A2UIProvider,可直接作为 BubbleContent 的 children 使用。
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```tsx
|
|
117
|
+
* // 作为 BubbleContent 的 children 使用
|
|
118
|
+
* <BubbleContent content={content} status={status}>
|
|
119
|
+
* <A2UISurface
|
|
120
|
+
* messages={a2uiMessages}
|
|
121
|
+
* surfaceId="main"
|
|
122
|
+
* onAction={(msg) => console.log('用户操作:', msg)}
|
|
123
|
+
* />
|
|
124
|
+
* </BubbleContent>
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export const A2UISurface: React.FC<A2UISurfaceProps> = ({
|
|
128
|
+
messages,
|
|
129
|
+
surfaceId = 'main',
|
|
130
|
+
onAction,
|
|
131
|
+
registry,
|
|
132
|
+
className,
|
|
133
|
+
style,
|
|
134
|
+
}) => {
|
|
135
|
+
const handleAction: OnActionCallback = useCallback((message) => {
|
|
136
|
+
onAction?.(message);
|
|
137
|
+
}, [onAction]);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<A2UIProvider onAction={handleAction}>
|
|
141
|
+
<A2UISurfaceInner
|
|
142
|
+
messages={messages}
|
|
143
|
+
surfaceId={surfaceId}
|
|
144
|
+
registry={registry}
|
|
145
|
+
className={className}
|
|
146
|
+
style={style}
|
|
147
|
+
/>
|
|
148
|
+
</A2UIProvider>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A2UIStaticViewer - A2UI 静态 JSON 渲染组件
|
|
154
|
+
*
|
|
155
|
+
* 用于直接从静态的组件定义和数据渲染 UI,无需流式消息。
|
|
156
|
+
* 适用于已有完整 A2UI 组件树的场景。
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```tsx
|
|
160
|
+
* const components = [
|
|
161
|
+
* { id: 'root', component: { Card: { child: 'text' } } },
|
|
162
|
+
* { id: 'text', component: { Text: { text: { path: '/message' } } } },
|
|
163
|
+
* ];
|
|
164
|
+
*
|
|
165
|
+
* <A2UIStaticViewer
|
|
166
|
+
* root="root"
|
|
167
|
+
* components={components}
|
|
168
|
+
* data={{ message: 'Hello World!' }}
|
|
169
|
+
* onAction={(action) => console.log('Action:', action)}
|
|
170
|
+
* />
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export const A2UIStaticViewer: React.FC<A2UIStaticViewerProps> = (props) => {
|
|
174
|
+
return (
|
|
175
|
+
<A2UIContainer>
|
|
176
|
+
<A2UIViewer {...props} />
|
|
177
|
+
</A2UIContainer>
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export default A2UISurface;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { A2UISurface, A2UIStaticViewer, type A2UISurfaceProps, type A2UIStaticViewerProps, type A2UIMessage } from './A2UIRenderer';
|
|
@@ -3,6 +3,7 @@ import { Button, Input, InputNumber, Switch, Space } from 'antd';
|
|
|
3
3
|
import { PlusOutlined, MinusOutlined } from '@ant-design/icons';
|
|
4
4
|
import { ArrayFieldProps, CustomParamSchema, FileSubType, sortPropertiesByOrder } from './types';
|
|
5
5
|
import styled from 'styled-components';
|
|
6
|
+
import { useTranslation } from '../../../i18n';
|
|
6
7
|
|
|
7
8
|
// 图片类型列表
|
|
8
9
|
const IMAGE_TYPES: FileSubType[] = ['jpg', 'png', 'svg'];
|
|
@@ -191,7 +192,8 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
|
|
|
191
192
|
}) => {
|
|
192
193
|
const { Title, Description, Items } = schema;
|
|
193
194
|
const displayTitle = Title || name;
|
|
194
|
-
|
|
195
|
+
const { t } = useTranslation();
|
|
196
|
+
|
|
195
197
|
// 计算当前数组的完整路径
|
|
196
198
|
const currentPath = fieldPath ? `${fieldPath}.${name}` : name;
|
|
197
199
|
|
|
@@ -370,7 +372,7 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
|
|
|
370
372
|
return sortedProperties.map(([propertyName, propertySchema]) => {
|
|
371
373
|
const isRequired = itemRequired.includes(propertyName);
|
|
372
374
|
return (
|
|
373
|
-
<React.Suspense key={propertyName} fallback={<div
|
|
375
|
+
<React.Suspense key={propertyName} fallback={<div>{t('common.loading')}</div>}>
|
|
374
376
|
<FieldRenderer
|
|
375
377
|
name={propertyName}
|
|
376
378
|
schema={propertySchema}
|
|
@@ -392,7 +394,7 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
|
|
|
392
394
|
// 渲染嵌套数组类型的数组项
|
|
393
395
|
const renderArrayItemContent = (item: any, index: number) => {
|
|
394
396
|
return (
|
|
395
|
-
<React.Suspense fallback={<div
|
|
397
|
+
<React.Suspense fallback={<div>{t('common.loading')}</div>}>
|
|
396
398
|
<FieldRenderer
|
|
397
399
|
name={`${name}[${index}]`}
|
|
398
400
|
schema={Items}
|
|
@@ -412,7 +414,7 @@ export const ArrayField: React.FC<ArrayFieldProps> = ({
|
|
|
412
414
|
// 渲染复杂类型的数组项(file、time、带枚举的类型)
|
|
413
415
|
const renderComplexItemContent = (item: any, index: number) => {
|
|
414
416
|
return (
|
|
415
|
-
<React.Suspense fallback={<div
|
|
417
|
+
<React.Suspense fallback={<div>{t('common.loading')}</div>}>
|
|
416
418
|
<FieldRenderer
|
|
417
419
|
name={`[${index}]`}
|
|
418
420
|
schema={Items}
|
|
@@ -3,6 +3,7 @@ import { Select, Checkbox, Radio } from 'antd';
|
|
|
3
3
|
import type { RadioChangeEvent } from 'antd';
|
|
4
4
|
import { EnumFieldProps, EnumDisplayStyle } from './types';
|
|
5
5
|
import styled from 'styled-components';
|
|
6
|
+
import { useTranslation } from '../../../i18n';
|
|
6
7
|
|
|
7
8
|
const { Option } = Select;
|
|
8
9
|
|
|
@@ -70,7 +71,8 @@ export const EnumField: React.FC<EnumFieldProps> = ({
|
|
|
70
71
|
disabled = false,
|
|
71
72
|
}) => {
|
|
72
73
|
const { Type } = schema;
|
|
73
|
-
|
|
74
|
+
const { t } = useTranslation();
|
|
75
|
+
|
|
74
76
|
// 优先从 AssociationPropertyMetadata 中读取,兼容旧的字段
|
|
75
77
|
const enumValues = useMemo(() => {
|
|
76
78
|
return schema.AssociationPropertyMetadata?.EnumValues || schema.EnumValues || [];
|
|
@@ -152,7 +154,7 @@ export const EnumField: React.FC<EnumFieldProps> = ({
|
|
|
152
154
|
onChange={handleSelectChange}
|
|
153
155
|
disabled={disabled}
|
|
154
156
|
mode="multiple"
|
|
155
|
-
placeholder={
|
|
157
|
+
placeholder={t('common.placeholderSelect')}
|
|
156
158
|
style={{ width: '100%' }}
|
|
157
159
|
allowClear
|
|
158
160
|
>
|
|
@@ -177,7 +179,7 @@ export const EnumField: React.FC<EnumFieldProps> = ({
|
|
|
177
179
|
onChange={handleSelectChange}
|
|
178
180
|
disabled={disabled}
|
|
179
181
|
mode={isMultiple ? 'multiple' : undefined}
|
|
180
|
-
placeholder={
|
|
182
|
+
placeholder={t('common.placeholderSelect')}
|
|
181
183
|
style={{ width: '100%' }}
|
|
182
184
|
allowClear
|
|
183
185
|
>
|
|
@@ -7,6 +7,7 @@ import { EnumField } from './EnumField';
|
|
|
7
7
|
import styled from 'styled-components';
|
|
8
8
|
import TimeField from './TimeField';
|
|
9
9
|
import FileField from './FileField';
|
|
10
|
+
import { useTranslation } from '../../../i18n';
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
// ==================== Styled Components ====================
|
|
@@ -130,7 +131,8 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
|
|
|
130
131
|
}) => {
|
|
131
132
|
const { Type, Title, Description } = schema;
|
|
132
133
|
const displayTitle = Title || name;
|
|
133
|
-
|
|
134
|
+
const { t } = useTranslation();
|
|
135
|
+
|
|
134
136
|
// 获取 Ant Design 的 prefixCls 配置,自动继承用户项目的 ConfigProvider 设置
|
|
135
137
|
// 使用 ConfigProvider.ConfigContext 获取完整配置
|
|
136
138
|
const configContext = useContext(ConfigProvider.ConfigContext);
|
|
@@ -176,7 +178,7 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
|
|
|
176
178
|
value={value}
|
|
177
179
|
onChange={(e) => handleChange(e.target.value)}
|
|
178
180
|
disabled={disabled}
|
|
179
|
-
placeholder={
|
|
181
|
+
placeholder={t('humanVerify.placeholder.input', { title: displayTitle })}
|
|
180
182
|
/>
|
|
181
183
|
</InputWrapper>
|
|
182
184
|
);
|
|
@@ -189,7 +191,7 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
|
|
|
189
191
|
onChange={handleChange}
|
|
190
192
|
disabled={disabled}
|
|
191
193
|
style={{ width: '100%' }}
|
|
192
|
-
placeholder={
|
|
194
|
+
placeholder={t('humanVerify.placeholder.input', { title: displayTitle })}
|
|
193
195
|
/>
|
|
194
196
|
</InputWrapper>
|
|
195
197
|
);
|
|
@@ -307,7 +309,7 @@ export const FieldRenderer: React.FC<FieldRendererProps> = ({
|
|
|
307
309
|
value={value}
|
|
308
310
|
onChange={(e) => handleChange(e.target.value)}
|
|
309
311
|
disabled={disabled}
|
|
310
|
-
placeholder={
|
|
312
|
+
placeholder={t('humanVerify.placeholder.input', { title: displayTitle })}
|
|
311
313
|
/>
|
|
312
314
|
</InputWrapper>
|
|
313
315
|
);
|