@alicloud/appflow-chat 0.0.1-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/README-ZH.md +188 -0
- package/README.md +190 -0
- package/dist/appflow-chat.cjs.js +1903 -0
- package/dist/appflow-chat.esm.js +36965 -0
- package/dist/types/index.d.ts +862 -0
- package/package.json +87 -0
- package/src/components/DocReferences.tsx +64 -0
- package/src/components/HumanVerify/CustomParamsRenderer/ArrayField.tsx +394 -0
- package/src/components/HumanVerify/CustomParamsRenderer/FieldRenderer.tsx +202 -0
- package/src/components/HumanVerify/CustomParamsRenderer/ObjectField.tsx +126 -0
- package/src/components/HumanVerify/CustomParamsRenderer/index.tsx +166 -0
- package/src/components/HumanVerify/CustomParamsRenderer/types.ts +203 -0
- package/src/components/HumanVerify/HistoryCard.tsx +156 -0
- package/src/components/HumanVerify/HumanVerify.tsx +184 -0
- package/src/components/HumanVerify/index.ts +11 -0
- package/src/components/MarkdownRenderer.tsx +195 -0
- package/src/components/MessageBubble.tsx +400 -0
- package/src/components/RichMessageBubble.tsx +283 -0
- package/src/components/WebSearchPanel.tsx +68 -0
- package/src/context/RichBubble.tsx +21 -0
- package/src/core/BubbleContent.tsx +75 -0
- package/src/core/RichBubbleContent.tsx +324 -0
- package/src/core/SourceContent.tsx +285 -0
- package/src/core/WebSearchContent.tsx +219 -0
- package/src/core/index.ts +16 -0
- package/src/hooks/usePreSignUpload.ts +36 -0
- package/src/index.ts +80 -0
- package/src/markdown/components/Chart.tsx +120 -0
- package/src/markdown/components/Error.tsx +39 -0
- package/src/markdown/components/FileDisplay.tsx +246 -0
- package/src/markdown/components/Loading.tsx +41 -0
- package/src/markdown/components/SyntaxHighlight.tsx +182 -0
- package/src/markdown/index.tsx +250 -0
- package/src/markdown/styled.ts +234 -0
- package/src/markdown/utils/dataProcessor.ts +89 -0
- package/src/services/ChatService.ts +926 -0
- package/src/utils/fetchEventSource.ts +65 -0
- package/src/utils/loadEcharts.ts +32 -0
- package/src/utils/loadPrism.ts +156 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSearchPanel - SDK封装的网页搜索结果组件
|
|
3
|
+
* 内部复用 WebSearchContent 核心组件,提供简化的接口
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { WebSearchContent, WebSearchItem } from '../core';
|
|
8
|
+
|
|
9
|
+
// 重新导出类型,保持向后兼容
|
|
10
|
+
export type { WebSearchItem };
|
|
11
|
+
|
|
12
|
+
export interface WebSearchPanelProps {
|
|
13
|
+
/** 搜索结果列表 */
|
|
14
|
+
items: WebSearchItem[];
|
|
15
|
+
/** 是否显示 */
|
|
16
|
+
open: boolean;
|
|
17
|
+
/** 关闭回调 */
|
|
18
|
+
onClose: () => void;
|
|
19
|
+
/** 面板宽度 */
|
|
20
|
+
width?: number | string;
|
|
21
|
+
/** 挂载容器 */
|
|
22
|
+
getContainer?: () => HTMLElement;
|
|
23
|
+
/** 自定义类名 */
|
|
24
|
+
className?: string;
|
|
25
|
+
/** 自定义样式 */
|
|
26
|
+
style?: React.CSSProperties;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* WebSearchPanel - 网页搜索结果面板组件
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* const [open, setOpen] = useState(false);
|
|
35
|
+
* const [searchResults, setSearchResults] = useState([]);
|
|
36
|
+
*
|
|
37
|
+
* <WebSearchPanel
|
|
38
|
+
* items={searchResults}
|
|
39
|
+
* open={open}
|
|
40
|
+
* onClose={() => setOpen(false)}
|
|
41
|
+
* width={400}
|
|
42
|
+
* />
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export const WebSearchPanel: React.FC<WebSearchPanelProps> = ({
|
|
46
|
+
items,
|
|
47
|
+
open,
|
|
48
|
+
onClose,
|
|
49
|
+
width = 400,
|
|
50
|
+
getContainer,
|
|
51
|
+
className,
|
|
52
|
+
style,
|
|
53
|
+
}) => {
|
|
54
|
+
return (
|
|
55
|
+
<WebSearchContent
|
|
56
|
+
className={`appflow-sdk-web-search-panel ${className || ''}`}
|
|
57
|
+
style={style}
|
|
58
|
+
items={items}
|
|
59
|
+
open={open}
|
|
60
|
+
onClose={onClose}
|
|
61
|
+
isPageConfig={false}
|
|
62
|
+
width={width}
|
|
63
|
+
getContainer={getContainer}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default WebSearchPanel;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RichBubble 上下文
|
|
3
|
+
* 用于在 RichBubbleContent 和子组件(如 ECharts)之间共享折叠面板状态
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createContext, useContext } from 'react';
|
|
7
|
+
|
|
8
|
+
export interface RichBubbleContextValue {
|
|
9
|
+
/** 当前激活的折叠面板 key */
|
|
10
|
+
activeKey: string | string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const RichBubbleContext = createContext<RichBubbleContextValue>({
|
|
14
|
+
activeKey: [],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const RichBubbleProvider = RichBubbleContext.Provider;
|
|
18
|
+
|
|
19
|
+
export const useRichBubbleContext = () => useContext(RichBubbleContext);
|
|
20
|
+
|
|
21
|
+
export default RichBubbleContext;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BubbleContent - 消息气泡核心展示组件
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import styled from 'styled-components';
|
|
7
|
+
import { MarkdownView } from '../markdown';
|
|
8
|
+
|
|
9
|
+
export interface BubbleContentProps {
|
|
10
|
+
/** 消息内容(Markdown格式) */
|
|
11
|
+
content: string;
|
|
12
|
+
/** 渲染状态 */
|
|
13
|
+
status?: 'Running' | 'Success' | 'Error';
|
|
14
|
+
/** 消息角色 */
|
|
15
|
+
role?: 'user' | 'bot';
|
|
16
|
+
/** 自定义类名 */
|
|
17
|
+
className?: string;
|
|
18
|
+
/** 自定义样式 */
|
|
19
|
+
style?: React.CSSProperties;
|
|
20
|
+
/** 自定义气泡样式(用于业务组件传入) */
|
|
21
|
+
bubbleStyle?: React.CSSProperties;
|
|
22
|
+
/** 子元素(如参考资料组件) */
|
|
23
|
+
children?: React.ReactNode;
|
|
24
|
+
/** 等待消息文本,由外层传入 */
|
|
25
|
+
waitingMessage?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Markdown 内容容器样式
|
|
29
|
+
const StyledMarkdownView = styled.div<{ $role: string }>`
|
|
30
|
+
white-space: normal;
|
|
31
|
+
li {
|
|
32
|
+
p {
|
|
33
|
+
display: block !important;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* BubbleContent - 消息气泡核心展示组件
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* <BubbleContent
|
|
44
|
+
* content="这是消息内容,支持**Markdown**"
|
|
45
|
+
* status="Success"
|
|
46
|
+
* role="bot"
|
|
47
|
+
* >
|
|
48
|
+
* <ChatBotSource items={references} />
|
|
49
|
+
* </BubbleContent>
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const BubbleContent: React.FC<BubbleContentProps> = ({
|
|
53
|
+
content,
|
|
54
|
+
status = 'Success',
|
|
55
|
+
role = 'bot',
|
|
56
|
+
className,
|
|
57
|
+
style,
|
|
58
|
+
children,
|
|
59
|
+
waitingMessage,
|
|
60
|
+
}) => {
|
|
61
|
+
return (
|
|
62
|
+
<div className={className} style={style}>
|
|
63
|
+
<StyledMarkdownView $role={role}>
|
|
64
|
+
<MarkdownView
|
|
65
|
+
content={content}
|
|
66
|
+
status={status}
|
|
67
|
+
waitingMessage={waitingMessage}
|
|
68
|
+
/>
|
|
69
|
+
</StyledMarkdownView>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export default BubbleContent;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RichBubbleContent - 富文本消息气泡核心展示组件
|
|
3
|
+
* 纯展示组件,不包含业务逻辑(如 Modal、bot 上下文等)
|
|
4
|
+
*
|
|
5
|
+
* 支持的消息类型:
|
|
6
|
+
* - markdown: Markdown格式文本
|
|
7
|
+
* - rich: 包含多种类型的复合消息
|
|
8
|
+
* - ant_table: Ant Design表格数据
|
|
9
|
+
* - code: 代码块
|
|
10
|
+
* - echart: ECharts图表
|
|
11
|
+
* - step: 步骤折叠面板
|
|
12
|
+
* - error: 错误信息
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import React, { useEffect, useState } from 'react';
|
|
17
|
+
import styled from 'styled-components';
|
|
18
|
+
import { Collapse } from 'antd';
|
|
19
|
+
import { MarkdownView } from '../markdown';
|
|
20
|
+
import { convertTableDataToMarkdown, processStepContent } from '../markdown/utils/dataProcessor';
|
|
21
|
+
import { RichBubbleProvider } from '../context/RichBubble';
|
|
22
|
+
|
|
23
|
+
export interface RichBubbleContentProps {
|
|
24
|
+
/** 消息内容 */
|
|
25
|
+
content: string;
|
|
26
|
+
/** 消息类型 */
|
|
27
|
+
messageType?: 'markdown' | 'rich';
|
|
28
|
+
/** 渲染状态 */
|
|
29
|
+
status?: 'Running' | 'Success' | 'Error';
|
|
30
|
+
/** 消息角色 */
|
|
31
|
+
role?: 'user' | 'bot';
|
|
32
|
+
/** 自定义类名 */
|
|
33
|
+
className?: string;
|
|
34
|
+
/** 自定义样式 */
|
|
35
|
+
style?: React.CSSProperties;
|
|
36
|
+
/** 子元素(如参考资料组件) */
|
|
37
|
+
children?: React.ReactNode;
|
|
38
|
+
/** 等待消息文本,由外层传入 */
|
|
39
|
+
waitingMessage?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Markdown 内容容器样式
|
|
43
|
+
const StyledMarkdownView = styled.div<{ $role: string }>`
|
|
44
|
+
white-space: normal;
|
|
45
|
+
li {
|
|
46
|
+
p {
|
|
47
|
+
display: block !important;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
// 步骤样式
|
|
53
|
+
const StyledStepTitleWrapper = styled.div`
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
gap: 8px;
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const StyledStatusIcon = styled.span<{ $success: boolean }>`
|
|
60
|
+
color: ${props => props.$success ? '#52c41a' : '#1890ff'};
|
|
61
|
+
font-weight: bold;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
const StyledStepNumber = styled.span`
|
|
65
|
+
color: #666;
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const StyledStepText = styled.span`
|
|
70
|
+
color: #333;
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const StyledStepContent = styled.div`
|
|
74
|
+
padding: 8px 0;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* RichBubbleContent - 富文本消息气泡核心展示组件
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* // Markdown类型
|
|
83
|
+
* <RichBubbleContent
|
|
84
|
+
* content="# 标题\n正文内容"
|
|
85
|
+
* messageType="markdown"
|
|
86
|
+
* status="Success"
|
|
87
|
+
* />
|
|
88
|
+
*
|
|
89
|
+
* // Rich类型(包含多种内容)
|
|
90
|
+
* <RichBubbleContent
|
|
91
|
+
* content={JSON.stringify([
|
|
92
|
+
* { messageType: 'markdown', content: '# 分析结果' },
|
|
93
|
+
* { messageType: 'ant_table', content: JSON.stringify({ column: ['A', 'B'], data: [{A: 1, B: 2}] }) },
|
|
94
|
+
* { messageType: 'echart', content: JSON.stringify({ option: {...} }) }
|
|
95
|
+
* ])}
|
|
96
|
+
* messageType="rich"
|
|
97
|
+
* status="Success"
|
|
98
|
+
* >
|
|
99
|
+
* <DocReferences items={references} />
|
|
100
|
+
* </RichBubbleContent>
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export const RichBubbleContent: React.FC<RichBubbleContentProps> = ({
|
|
104
|
+
content,
|
|
105
|
+
messageType = 'markdown',
|
|
106
|
+
status = 'Success',
|
|
107
|
+
role = 'bot',
|
|
108
|
+
className,
|
|
109
|
+
style,
|
|
110
|
+
children,
|
|
111
|
+
waitingMessage,
|
|
112
|
+
}) => {
|
|
113
|
+
// step消息折叠面板激活的索引
|
|
114
|
+
const [activeKey, setActiveKey] = useState<string | string[]>([]);
|
|
115
|
+
|
|
116
|
+
// 初始化step消息折叠面板激活的索引
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (messageType === 'rich') {
|
|
119
|
+
try {
|
|
120
|
+
const richContent = JSON.parse(content);
|
|
121
|
+
if (Array.isArray(richContent)) {
|
|
122
|
+
const newActiveKeys = richContent
|
|
123
|
+
.filter(item => item?.messageType === 'step' && item?.content)
|
|
124
|
+
.map(item => {
|
|
125
|
+
try {
|
|
126
|
+
const itemContent = JSON.parse(item?.content);
|
|
127
|
+
return itemContent?.id;
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.filter(Boolean);
|
|
133
|
+
|
|
134
|
+
// 只有当有新的keys时才更新,避免无限循环
|
|
135
|
+
if (newActiveKeys.length > 0) {
|
|
136
|
+
setActiveKey(newActiveKeys);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('解析rich类型消息失败', error);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}, [content, messageType]);
|
|
144
|
+
|
|
145
|
+
// 渲染步骤内容
|
|
146
|
+
const renderStepContent = (detail: any, type: string): React.ReactNode => {
|
|
147
|
+
if (type === 'rich') {
|
|
148
|
+
// 递归调用renderContent处理rich类型
|
|
149
|
+
return renderContent(type, detail);
|
|
150
|
+
} else {
|
|
151
|
+
const contentToRender = detail || '';
|
|
152
|
+
if (!contentToRender) {
|
|
153
|
+
return <StyledStepContent>暂无内容</StyledStepContent>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<StyledStepContent>
|
|
158
|
+
<MarkdownView content={processStepContent(detail, type)} waitingMessage={waitingMessage} />
|
|
159
|
+
</StyledStepContent>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// 渲染内容
|
|
165
|
+
const renderContent = (msgType: string, text: string): React.ReactNode => {
|
|
166
|
+
if (msgType === 'markdown' && text) {
|
|
167
|
+
return (
|
|
168
|
+
<MarkdownView
|
|
169
|
+
content={text}
|
|
170
|
+
status={status}
|
|
171
|
+
waitingMessage={waitingMessage}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (msgType === 'rich') {
|
|
177
|
+
try {
|
|
178
|
+
const richContent = JSON.parse(text);
|
|
179
|
+
if (!richContent || !Array.isArray(richContent)) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return richContent?.map((item, index) => {
|
|
184
|
+
const itemMessageType = item?.messageType;
|
|
185
|
+
|
|
186
|
+
// markdown类型
|
|
187
|
+
if (itemMessageType === 'markdown' && item?.content) {
|
|
188
|
+
return (
|
|
189
|
+
<MarkdownView
|
|
190
|
+
key={`markdown-${index}`}
|
|
191
|
+
content={item?.content}
|
|
192
|
+
status={status}
|
|
193
|
+
waitingMessage={waitingMessage}
|
|
194
|
+
/>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ant_table类型
|
|
199
|
+
if (itemMessageType === 'ant_table' && item?.content) {
|
|
200
|
+
try {
|
|
201
|
+
const parsedContent = JSON.parse(item?.content);
|
|
202
|
+
const antTableContent = convertTableDataToMarkdown(parsedContent);
|
|
203
|
+
return (
|
|
204
|
+
<MarkdownView
|
|
205
|
+
key={`table-${index}`}
|
|
206
|
+
content={antTableContent}
|
|
207
|
+
status={status}
|
|
208
|
+
waitingMessage={waitingMessage}
|
|
209
|
+
/>
|
|
210
|
+
);
|
|
211
|
+
} catch {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// code类型
|
|
217
|
+
if (itemMessageType === 'code' && item?.content) {
|
|
218
|
+
const codeContent = '```sql\n' + item?.content + '\n```';
|
|
219
|
+
return (
|
|
220
|
+
<MarkdownView
|
|
221
|
+
key={`code-${index}`}
|
|
222
|
+
content={codeContent}
|
|
223
|
+
status={status}
|
|
224
|
+
waitingMessage={waitingMessage}
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// echart类型
|
|
230
|
+
if (itemMessageType === 'echart') {
|
|
231
|
+
const echartContent = '```echarts\n' + item?.content + '\n```';
|
|
232
|
+
return (
|
|
233
|
+
<MarkdownView
|
|
234
|
+
key={`echart-${index}`}
|
|
235
|
+
content={echartContent}
|
|
236
|
+
status={status}
|
|
237
|
+
waitingMessage={waitingMessage}
|
|
238
|
+
/>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// step类型
|
|
243
|
+
if (itemMessageType === 'step') {
|
|
244
|
+
try {
|
|
245
|
+
const itemContent = JSON.parse(item?.content);
|
|
246
|
+
const collapseItems = [{
|
|
247
|
+
key: itemContent?.id,
|
|
248
|
+
label: (
|
|
249
|
+
<StyledStepTitleWrapper>
|
|
250
|
+
<StyledStatusIcon $success={status === 'Success'}>
|
|
251
|
+
{status === 'Success' ? '✓' : '⟳'}
|
|
252
|
+
</StyledStatusIcon>
|
|
253
|
+
<StyledStepNumber>步骤{index + 1}:</StyledStepNumber>
|
|
254
|
+
<StyledStepText>{itemContent?.title}</StyledStepText>
|
|
255
|
+
</StyledStepTitleWrapper>
|
|
256
|
+
),
|
|
257
|
+
children: renderStepContent(itemContent?.detail, itemContent?.messageType),
|
|
258
|
+
style: {
|
|
259
|
+
marginBottom: '8px',
|
|
260
|
+
backgroundColor: 'white',
|
|
261
|
+
borderRadius: '8px',
|
|
262
|
+
border: '1px solid #e8e8e8'
|
|
263
|
+
}
|
|
264
|
+
}];
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<Collapse
|
|
268
|
+
key={`step-${index}`}
|
|
269
|
+
activeKey={activeKey}
|
|
270
|
+
size="small"
|
|
271
|
+
ghost={false}
|
|
272
|
+
items={collapseItems}
|
|
273
|
+
expandIconPosition="end"
|
|
274
|
+
style={{
|
|
275
|
+
backgroundColor: 'transparent',
|
|
276
|
+
border: 'none'
|
|
277
|
+
}}
|
|
278
|
+
onChange={(keys) => {
|
|
279
|
+
setActiveKey(keys);
|
|
280
|
+
}}
|
|
281
|
+
/>
|
|
282
|
+
);
|
|
283
|
+
} catch {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// error类型
|
|
289
|
+
if (itemMessageType === 'error') {
|
|
290
|
+
const errorContent = `<span style="color:red">${item?.content ?? ''}</span>`;
|
|
291
|
+
return (
|
|
292
|
+
<MarkdownView
|
|
293
|
+
key={`error-${index}`}
|
|
294
|
+
content={errorContent}
|
|
295
|
+
status={status}
|
|
296
|
+
waitingMessage={waitingMessage}
|
|
297
|
+
/>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return null;
|
|
302
|
+
});
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.error('Error rendering message:', error);
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return null;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<RichBubbleProvider value={{ activeKey }}>
|
|
314
|
+
<div className={className} style={style}>
|
|
315
|
+
<StyledMarkdownView $role={role}>
|
|
316
|
+
{renderContent(messageType, content)}
|
|
317
|
+
</StyledMarkdownView>
|
|
318
|
+
{children}
|
|
319
|
+
</div>
|
|
320
|
+
</RichBubbleProvider>
|
|
321
|
+
);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
export default RichBubbleContent;
|