@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.
Files changed (40) hide show
  1. package/README-ZH.md +188 -0
  2. package/README.md +190 -0
  3. package/dist/appflow-chat.cjs.js +1903 -0
  4. package/dist/appflow-chat.esm.js +36965 -0
  5. package/dist/types/index.d.ts +862 -0
  6. package/package.json +87 -0
  7. package/src/components/DocReferences.tsx +64 -0
  8. package/src/components/HumanVerify/CustomParamsRenderer/ArrayField.tsx +394 -0
  9. package/src/components/HumanVerify/CustomParamsRenderer/FieldRenderer.tsx +202 -0
  10. package/src/components/HumanVerify/CustomParamsRenderer/ObjectField.tsx +126 -0
  11. package/src/components/HumanVerify/CustomParamsRenderer/index.tsx +166 -0
  12. package/src/components/HumanVerify/CustomParamsRenderer/types.ts +203 -0
  13. package/src/components/HumanVerify/HistoryCard.tsx +156 -0
  14. package/src/components/HumanVerify/HumanVerify.tsx +184 -0
  15. package/src/components/HumanVerify/index.ts +11 -0
  16. package/src/components/MarkdownRenderer.tsx +195 -0
  17. package/src/components/MessageBubble.tsx +400 -0
  18. package/src/components/RichMessageBubble.tsx +283 -0
  19. package/src/components/WebSearchPanel.tsx +68 -0
  20. package/src/context/RichBubble.tsx +21 -0
  21. package/src/core/BubbleContent.tsx +75 -0
  22. package/src/core/RichBubbleContent.tsx +324 -0
  23. package/src/core/SourceContent.tsx +285 -0
  24. package/src/core/WebSearchContent.tsx +219 -0
  25. package/src/core/index.ts +16 -0
  26. package/src/hooks/usePreSignUpload.ts +36 -0
  27. package/src/index.ts +80 -0
  28. package/src/markdown/components/Chart.tsx +120 -0
  29. package/src/markdown/components/Error.tsx +39 -0
  30. package/src/markdown/components/FileDisplay.tsx +246 -0
  31. package/src/markdown/components/Loading.tsx +41 -0
  32. package/src/markdown/components/SyntaxHighlight.tsx +182 -0
  33. package/src/markdown/index.tsx +250 -0
  34. package/src/markdown/styled.ts +234 -0
  35. package/src/markdown/utils/dataProcessor.ts +89 -0
  36. package/src/services/ChatService.ts +926 -0
  37. package/src/utils/fetchEventSource.ts +65 -0
  38. package/src/utils/loadEcharts.ts +32 -0
  39. package/src/utils/loadPrism.ts +156 -0
  40. 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;