@alicloud/appflow-chat 0.0.4-beta.5 → 0.0.4-beta.6
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 +157 -161
- package/dist/appflow-chat.esm.js +11980 -11526
- package/dist/types/index.d.ts +169 -0
- package/package.json +3 -15
- 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 +38 -101
- package/src/components/HumanVerify/HistoryCard.tsx +6 -4
- package/src/components/HumanVerify/HumanVerify.tsx +5 -3
- package/src/components/MessageBubble.tsx +4 -2
- 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 +25 -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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n 工具函数:插值、路径取值、深度合并
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { TranslationParams } from '../locales/types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 字符串插值
|
|
9
|
+
* @example
|
|
10
|
+
* interpolate('请输入{title}', { title: '名称' }) // '请输入名称'
|
|
11
|
+
*/
|
|
12
|
+
export function interpolate(template: string, params: TranslationParams): string {
|
|
13
|
+
return template.replace(/\{(\w+)\}/g, (_, key: string) => {
|
|
14
|
+
const value = params[key];
|
|
15
|
+
return value === undefined || value === null ? `{${key}}` : String(value);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 按点分路径从对象中取值
|
|
21
|
+
* @example
|
|
22
|
+
* getByPath({ a: { b: 1 } }, 'a.b') // 1
|
|
23
|
+
*/
|
|
24
|
+
export function getByPath(obj: unknown, path: string): unknown {
|
|
25
|
+
if (!obj || typeof obj !== 'object') return undefined;
|
|
26
|
+
return path.split('.').reduce<unknown>((acc, key) => {
|
|
27
|
+
if (acc && typeof acc === 'object' && key in acc) {
|
|
28
|
+
return (acc as Record<string, unknown>)[key];
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}, obj);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 深度合并两个对象(不修改原对象)
|
|
36
|
+
* 仅合并普通对象,数组和其他类型直接覆盖。
|
|
37
|
+
*/
|
|
38
|
+
export function deepMerge<T extends Record<string, unknown>>(
|
|
39
|
+
target: T,
|
|
40
|
+
source: Record<string, unknown> | undefined
|
|
41
|
+
): T {
|
|
42
|
+
if (!source) return target;
|
|
43
|
+
|
|
44
|
+
const result: Record<string, unknown> = { ...target };
|
|
45
|
+
|
|
46
|
+
for (const key of Object.keys(source)) {
|
|
47
|
+
const sourceValue = source[key];
|
|
48
|
+
const targetValue = result[key];
|
|
49
|
+
|
|
50
|
+
if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {
|
|
51
|
+
result[key] = deepMerge(
|
|
52
|
+
targetValue as Record<string, unknown>,
|
|
53
|
+
sourceValue as Record<string, unknown>
|
|
54
|
+
);
|
|
55
|
+
} else if (sourceValue !== undefined) {
|
|
56
|
+
result[key] = sourceValue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return result as T;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
64
|
+
if (value === null || typeof value !== 'object') return false;
|
|
65
|
+
const proto = Object.getPrototypeOf(value);
|
|
66
|
+
return proto === null || proto === Object.prototype;
|
|
67
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -79,3 +79,28 @@ export type {
|
|
|
79
79
|
// ==================== 工具函数导出 ====================
|
|
80
80
|
export { loadEchartsScript } from './utils/loadEcharts';
|
|
81
81
|
export { loadMermaidScript } from './utils/loadMermaid';
|
|
82
|
+
|
|
83
|
+
// ==================== 国际化(i18n)导出 ====================
|
|
84
|
+
export {
|
|
85
|
+
LocaleProvider,
|
|
86
|
+
LocaleContext,
|
|
87
|
+
useTranslation,
|
|
88
|
+
translate,
|
|
89
|
+
setGlobalLocale,
|
|
90
|
+
getGlobalLocale,
|
|
91
|
+
getGlobalLocaleName,
|
|
92
|
+
} from './i18n';
|
|
93
|
+
export type {
|
|
94
|
+
LocaleProviderProps,
|
|
95
|
+
LocaleContextValue,
|
|
96
|
+
UseTranslationResult,
|
|
97
|
+
} from './i18n';
|
|
98
|
+
|
|
99
|
+
// ==================== 国际化词条导出 ====================
|
|
100
|
+
export { zhCN, enUS } from './locales';
|
|
101
|
+
export type {
|
|
102
|
+
Locale,
|
|
103
|
+
TranslationKey,
|
|
104
|
+
TranslationParams,
|
|
105
|
+
DeepPartial,
|
|
106
|
+
} from './locales';
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 英文词条
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Locale } from './types';
|
|
6
|
+
|
|
7
|
+
const enUS: Locale = {
|
|
8
|
+
common: {
|
|
9
|
+
loading: 'Loading...',
|
|
10
|
+
confirm: 'OK',
|
|
11
|
+
cancel: 'Cancel',
|
|
12
|
+
submit: 'Submit',
|
|
13
|
+
retry: 'Retry',
|
|
14
|
+
copy: 'Copy',
|
|
15
|
+
copied: 'Copied',
|
|
16
|
+
placeholderSelect: 'Please select',
|
|
17
|
+
},
|
|
18
|
+
humanVerify: {
|
|
19
|
+
requiredAll: 'Please fill in all required fields',
|
|
20
|
+
submitted: 'Submitted',
|
|
21
|
+
pending: 'Pending',
|
|
22
|
+
placeholder: {
|
|
23
|
+
input: 'Please enter {title}',
|
|
24
|
+
select: 'Please select',
|
|
25
|
+
},
|
|
26
|
+
file: {
|
|
27
|
+
supportAll: 'Supports all file formats',
|
|
28
|
+
supportFormats: 'Supports {formats} formats',
|
|
29
|
+
uploadButton: 'Select File',
|
|
30
|
+
uploading: 'Uploading',
|
|
31
|
+
upload: 'Upload',
|
|
32
|
+
defaultFileName: 'File',
|
|
33
|
+
maxSizeError: 'File size cannot exceed {size}',
|
|
34
|
+
uploadFailed: 'File upload failed',
|
|
35
|
+
tokenFailed: 'Failed to get upload credentials',
|
|
36
|
+
uploaderNotConfigured: 'Upload feature is not configured',
|
|
37
|
+
uploadMethodNotConfigured: 'File upload method is not configured',
|
|
38
|
+
getFileIdFailed: 'Failed to get file ID',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
webSearch: {
|
|
42
|
+
title: 'Search Results',
|
|
43
|
+
foundPages: 'Found {count} web pages',
|
|
44
|
+
},
|
|
45
|
+
source: {
|
|
46
|
+
title: 'References',
|
|
47
|
+
answerFrom: 'Sources:',
|
|
48
|
+
imageFrom: 'Images:',
|
|
49
|
+
},
|
|
50
|
+
rich: {
|
|
51
|
+
stepLabel: 'Step {index}:',
|
|
52
|
+
emptyContent: 'No content',
|
|
53
|
+
},
|
|
54
|
+
markdown: {
|
|
55
|
+
copyCode: 'Copy code',
|
|
56
|
+
copied: 'Copied!',
|
|
57
|
+
copy: 'Copy',
|
|
58
|
+
copiedShort: 'Copied',
|
|
59
|
+
deepThinking: 'Deep Thinking',
|
|
60
|
+
chartLoading: 'Loading chart...',
|
|
61
|
+
tableLoading: 'Loading table...',
|
|
62
|
+
tableLoadFailed: 'Failed to load table data, please check the data format',
|
|
63
|
+
chartLoadFailed: 'Failed to load chart data, please check the data format',
|
|
64
|
+
mermaidLoadFailed: 'Failed to load Mermaid library',
|
|
65
|
+
mermaidNotLoaded: 'Mermaid is not loaded correctly',
|
|
66
|
+
mermaidRenderFailed: 'Mermaid chart rendering failed',
|
|
67
|
+
},
|
|
68
|
+
message: {
|
|
69
|
+
like: 'Like',
|
|
70
|
+
dislike: 'Dislike',
|
|
71
|
+
regenerate: 'Regenerate',
|
|
72
|
+
copy: 'Copy',
|
|
73
|
+
copied: 'Copied',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default enUS;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locale 类型定义
|
|
3
|
+
*
|
|
4
|
+
* 以 zh-CN 词条结构作为类型基准,所有其他语言必须实现相同的 key 结构。
|
|
5
|
+
* - 对象结构(哪些 key、嵌套层级)严格等同 zhCN
|
|
6
|
+
* - 叶子节点的值放宽为 string,避免英文字面量被中文字面量类型卡住
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type zhCN from './zh-CN';
|
|
10
|
+
|
|
11
|
+
/** 词条对象类型:保留 zhCN 的结构,但叶子节点统一为 string */
|
|
12
|
+
export type Locale = DeepStringify<typeof zhCN>;
|
|
13
|
+
|
|
14
|
+
/** 所有合法的扁平 key 路径,如 'common.loading' | 'humanVerify.placeholder.input' */
|
|
15
|
+
export type TranslationKey = DeepKey<typeof zhCN>;
|
|
16
|
+
|
|
17
|
+
/** 插值参数 */
|
|
18
|
+
export type TranslationParams = Record<string, string | number>;
|
|
19
|
+
|
|
20
|
+
/** 深度可选,用于 LocaleProvider 的 overrides 字段 */
|
|
21
|
+
export type DeepPartial<T> = {
|
|
22
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** 把所有叶子节点的值类型统一为 string,并去掉 readonly */
|
|
26
|
+
type DeepStringify<T> = {
|
|
27
|
+
-readonly [K in keyof T]: T[K] extends object ? DeepStringify<T[K]> : string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** 递归生成所有叶子节点的扁平路径联合类型 */
|
|
31
|
+
type DeepKey<T, P extends string = ''> = {
|
|
32
|
+
[K in keyof T & string]: T[K] extends object
|
|
33
|
+
? DeepKey<T[K], `${P}${P extends '' ? '' : '.'}${K}`>
|
|
34
|
+
: `${P}${P extends '' ? '' : '.'}${K}`;
|
|
35
|
+
}[keyof T & string];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 中文词条(默认语言)
|
|
3
|
+
*
|
|
4
|
+
* 该文件作为 Locale 类型的基准结构,所有其他语言文件必须实现相同的 key 结构。
|
|
5
|
+
* 新增文案时优先在此处添加,再补齐其他语言文件。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const zhCN = {
|
|
9
|
+
common: {
|
|
10
|
+
loading: '加载中...',
|
|
11
|
+
confirm: '确定',
|
|
12
|
+
cancel: '取消',
|
|
13
|
+
submit: '提交',
|
|
14
|
+
retry: '重试',
|
|
15
|
+
copy: '复制',
|
|
16
|
+
copied: '已复制',
|
|
17
|
+
placeholderSelect: '请选择',
|
|
18
|
+
},
|
|
19
|
+
humanVerify: {
|
|
20
|
+
requiredAll: '请填写所有必填项',
|
|
21
|
+
submitted: '已提交',
|
|
22
|
+
pending: '待提交',
|
|
23
|
+
placeholder: {
|
|
24
|
+
input: '请输入{title}',
|
|
25
|
+
select: '请选择',
|
|
26
|
+
},
|
|
27
|
+
file: {
|
|
28
|
+
supportAll: '支持所有文件格式',
|
|
29
|
+
supportFormats: '支持 {formats} 格式',
|
|
30
|
+
uploadButton: '选择文件',
|
|
31
|
+
uploading: '上传中',
|
|
32
|
+
upload: '上传',
|
|
33
|
+
defaultFileName: '文件',
|
|
34
|
+
maxSizeError: '文件大小不能超过 {size}',
|
|
35
|
+
uploadFailed: '文件上传失败',
|
|
36
|
+
tokenFailed: '获取上传凭证失败',
|
|
37
|
+
uploaderNotConfigured: '上传功能未配置',
|
|
38
|
+
uploadMethodNotConfigured: '文件上传方法未配置',
|
|
39
|
+
getFileIdFailed: '获取文件ID失败',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
webSearch: {
|
|
43
|
+
title: '搜索结果',
|
|
44
|
+
foundPages: '已搜索到{count}个网页',
|
|
45
|
+
},
|
|
46
|
+
source: {
|
|
47
|
+
title: '参考资料',
|
|
48
|
+
answerFrom: '回答来源:',
|
|
49
|
+
imageFrom: '图片来源:',
|
|
50
|
+
},
|
|
51
|
+
rich: {
|
|
52
|
+
stepLabel: '步骤{index}:',
|
|
53
|
+
emptyContent: '暂无内容',
|
|
54
|
+
},
|
|
55
|
+
markdown: {
|
|
56
|
+
copyCode: '复制代码',
|
|
57
|
+
copied: '已复制!',
|
|
58
|
+
copy: '复制',
|
|
59
|
+
copiedShort: '已复制',
|
|
60
|
+
deepThinking: '深度思考',
|
|
61
|
+
chartLoading: '图表加载中...',
|
|
62
|
+
tableLoading: '表格加载中...',
|
|
63
|
+
tableLoadFailed: '表格数据加载失败,请检查数据格式',
|
|
64
|
+
chartLoadFailed: '图表数据加载失败,请检查数据格式',
|
|
65
|
+
mermaidLoadFailed: 'Mermaid 库加载失败',
|
|
66
|
+
mermaidNotLoaded: 'Mermaid 未正确加载',
|
|
67
|
+
mermaidRenderFailed: 'Mermaid 图表渲染失败',
|
|
68
|
+
},
|
|
69
|
+
message: {
|
|
70
|
+
like: '点赞',
|
|
71
|
+
dislike: '点踩',
|
|
72
|
+
regenerate: '重新生成',
|
|
73
|
+
copy: '复制',
|
|
74
|
+
copied: '已复制',
|
|
75
|
+
},
|
|
76
|
+
} as const;
|
|
77
|
+
|
|
78
|
+
export default zhCN;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import React, { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { useRichBubbleContext } from '@/context/RichBubble';
|
|
4
4
|
import loadEchartsScript from '@/utils/loadEcharts';
|
|
5
|
+
import { useTranslation } from '@/i18n';
|
|
5
6
|
|
|
6
7
|
interface Iprops {
|
|
7
8
|
options: any;
|
|
@@ -10,6 +11,7 @@ interface Iprops {
|
|
|
10
11
|
export const Chart: React.FC<Iprops> = ({
|
|
11
12
|
options,
|
|
12
13
|
}) => {
|
|
14
|
+
const { t } = useTranslation();
|
|
13
15
|
const chartContainerRef = useRef(null);
|
|
14
16
|
const chartInstanceRef = useRef(null);
|
|
15
17
|
const [isEchartsLoaded, setIsEchartsLoaded] = useState(false);
|
|
@@ -99,7 +101,7 @@ export const Chart: React.FC<Iprops> = ({
|
|
|
99
101
|
color: '#666'
|
|
100
102
|
}}
|
|
101
103
|
>
|
|
102
|
-
|
|
104
|
+
{t('markdown.chartLoading')}
|
|
103
105
|
</div>
|
|
104
106
|
);
|
|
105
107
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import loadMermaidScript from '@/utils/loadMermaid';
|
|
3
|
+
import { useTranslation } from '@/i18n';
|
|
3
4
|
|
|
4
5
|
interface IProps {
|
|
5
6
|
code: string;
|
|
@@ -37,6 +38,7 @@ const isMermaidCodeComplete = (code: string): boolean => {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
41
|
+
const { t } = useTranslation();
|
|
40
42
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
41
43
|
const [svg, setSvg] = useState<string>('');
|
|
42
44
|
const [error, setError] = useState<string>('');
|
|
@@ -55,7 +57,7 @@ export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
|
55
57
|
setIsMermaidLoaded(true);
|
|
56
58
|
} catch (error) {
|
|
57
59
|
console.error('mermaid加载失败:', error);
|
|
58
|
-
setError('
|
|
60
|
+
setError(t('markdown.mermaidLoadFailed'));
|
|
59
61
|
setIsLoading(false);
|
|
60
62
|
}
|
|
61
63
|
};
|
|
@@ -78,7 +80,7 @@ export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
|
78
80
|
|
|
79
81
|
const mermaid = (window as any).mermaid;
|
|
80
82
|
if (!mermaid) {
|
|
81
|
-
setError('
|
|
83
|
+
setError(t('markdown.mermaidNotLoaded'));
|
|
82
84
|
setIsLoading(false);
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
@@ -143,7 +145,7 @@ export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
|
143
145
|
} else {
|
|
144
146
|
// 流式输出结束后,如果仍然有错误,才显示错误
|
|
145
147
|
console.error('Mermaid 渲染错误:', err);
|
|
146
|
-
setError(err?.message || '
|
|
148
|
+
setError(err?.message || t('markdown.mermaidRenderFailed'));
|
|
147
149
|
}
|
|
148
150
|
} finally {
|
|
149
151
|
setIsLoading(false);
|
|
@@ -176,7 +178,7 @@ export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
|
176
178
|
marginBottom: '10px',
|
|
177
179
|
}}
|
|
178
180
|
>
|
|
179
|
-
|
|
181
|
+
{t('markdown.chartLoading')}
|
|
180
182
|
</div>
|
|
181
183
|
);
|
|
182
184
|
}
|
|
@@ -202,7 +204,7 @@ export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
|
202
204
|
marginBottom: '8px'
|
|
203
205
|
}}
|
|
204
206
|
>
|
|
205
|
-
|
|
207
|
+
{t('markdown.mermaidRenderFailed')}
|
|
206
208
|
</div>
|
|
207
209
|
<div
|
|
208
210
|
style={{
|
|
@@ -238,7 +240,7 @@ export const Mermaid: React.FC<IProps> = ({ code }) => {
|
|
|
238
240
|
marginBottom: '10px',
|
|
239
241
|
}}
|
|
240
242
|
>
|
|
241
|
-
|
|
243
|
+
{t('markdown.chartLoading')}
|
|
242
244
|
</div>
|
|
243
245
|
);
|
|
244
246
|
}
|
package/src/markdown/index.tsx
CHANGED
|
@@ -14,6 +14,7 @@ import { convertTableDataToMarkdown } from './utils/dataProcessor';
|
|
|
14
14
|
import Loading from './components/Loading';
|
|
15
15
|
import Error from './components/Error';
|
|
16
16
|
import Mermaid from './components/Mermaid';
|
|
17
|
+
import { useTranslation } from '@/i18n';
|
|
17
18
|
|
|
18
19
|
export interface MarkdownViewProps {
|
|
19
20
|
/** Markdown 内容 */
|
|
@@ -32,6 +33,7 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
32
33
|
content,
|
|
33
34
|
waitingMessage = '',
|
|
34
35
|
}) => {
|
|
36
|
+
const { t } = useTranslation();
|
|
35
37
|
|
|
36
38
|
// markdown扩展配置
|
|
37
39
|
const markdownOptions: Options = useMemo(() => ({
|
|
@@ -47,7 +49,7 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
47
49
|
maxHeight: '150px',
|
|
48
50
|
objectFit: 'contain',
|
|
49
51
|
}}
|
|
50
|
-
src={props.src}
|
|
52
|
+
src={props.src}
|
|
51
53
|
alt={props.alt}
|
|
52
54
|
preview={{ zIndex: 1100 }}
|
|
53
55
|
/>
|
|
@@ -57,7 +59,7 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
57
59
|
// 判断是否为行内代码
|
|
58
60
|
// 行内代码没有 className,且 inline 为 true
|
|
59
61
|
const isInline = inline || (!className && !node?.properties?.className);
|
|
60
|
-
|
|
62
|
+
|
|
61
63
|
if (isInline) {
|
|
62
64
|
// 行内代码 - 使用简单的 <code> 标签样式
|
|
63
65
|
return (
|
|
@@ -98,12 +100,12 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
98
100
|
// 检查是否是流式输出导致的解析错误
|
|
99
101
|
if (!isValidJSON(String(props?.children))) {
|
|
100
102
|
return (
|
|
101
|
-
<Loading text={'
|
|
103
|
+
<Loading text={t('markdown.tableLoading')} />
|
|
102
104
|
);
|
|
103
105
|
}
|
|
104
106
|
// 真实错误的处理
|
|
105
107
|
console.error("ant_table数据解析错误:", error);
|
|
106
|
-
return <Error text={'
|
|
108
|
+
return <Error text={t('markdown.tableLoadFailed')} />;
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -142,7 +144,7 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
142
144
|
} catch (configError) {
|
|
143
145
|
// 图表配置解析错误
|
|
144
146
|
console.error("图表配置解析错误:", configError);
|
|
145
|
-
return <Error text={'
|
|
147
|
+
return <Error text={t('markdown.chartLoadFailed')} />;
|
|
146
148
|
}
|
|
147
149
|
} catch (error: any) {
|
|
148
150
|
// 如果是流式输出导致的json解析错误,显示加载状态
|
|
@@ -152,7 +154,7 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
152
154
|
|
|
153
155
|
// 其他真实错误的处理
|
|
154
156
|
console.error("图表数据解析错误:", error);
|
|
155
|
-
return <Error text={'
|
|
157
|
+
return <Error text={t('markdown.chartLoadFailed')} />;
|
|
156
158
|
}
|
|
157
159
|
}
|
|
158
160
|
|
|
@@ -201,7 +203,7 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
201
203
|
</a>
|
|
202
204
|
),
|
|
203
205
|
},
|
|
204
|
-
}), []);
|
|
206
|
+
}), [t]);
|
|
205
207
|
|
|
206
208
|
// 检查是否是完整的JSON字符串
|
|
207
209
|
const isValidJSON = (str: string) => {
|
|
@@ -216,9 +218,10 @@ export const MarkdownView: React.FC<MarkdownViewProps> = ({
|
|
|
216
218
|
|
|
217
219
|
// 将think标签转换为details标签
|
|
218
220
|
const convertThinkToDetails = (content: string) => {
|
|
221
|
+
const summary = t('markdown.deepThinking');
|
|
219
222
|
return content?.replace(
|
|
220
223
|
/<think>([\s\S]*?)<\/think>/g,
|
|
221
|
-
|
|
224
|
+
`<details open><summary>${summary}</summary><pre class="think">$1</pre></details>\n\n`
|
|
222
225
|
);
|
|
223
226
|
};
|
|
224
227
|
|