@alifd/chat 0.3.28-beta.5 → 0.3.28-beta.7
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/es/index.js +1 -1
- package/es/markdown/index.js +44 -70
- package/lib/index.js +1 -1
- package/lib/markdown/index.js +44 -70
- package/package.json +1 -1
package/es/index.js
CHANGED
|
@@ -31,4 +31,4 @@ export { default as RadioGroup } from './radio-group';
|
|
|
31
31
|
export { default as CheckboxGroup } from './checkbox-group';
|
|
32
32
|
export { default as Select } from './select';
|
|
33
33
|
export { default as Flip } from './flip';
|
|
34
|
-
export const version = '0.3.28-beta.
|
|
34
|
+
export const version = '0.3.28-beta.7';
|
package/es/markdown/index.js
CHANGED
|
@@ -27,96 +27,70 @@ const Markdown = forwardRef((_a, ref) => {
|
|
|
27
27
|
const [displayedContent, setDisplayedContent] = useState(() => (others === null || others === void 0 ? void 0 : others.typewriterEffect) ? '' : content);
|
|
28
28
|
const [contentQueue, setContentQueue] = useState(() => (others === null || others === void 0 ? void 0 : others.typewriterEffect) ? content.split('') : []);
|
|
29
29
|
const typeTimeoutRef = useRef(null);
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
// 清理定时器函数
|
|
31
|
+
const clearTypeTimer = () => {
|
|
32
|
+
if (typeTimeoutRef.current) {
|
|
33
|
+
clearTimeout(typeTimeoutRef.current);
|
|
34
|
+
typeTimeoutRef.current = null;
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// 消息队列更新
|
|
36
|
+
};
|
|
37
|
+
// 消息队列更新 - 仅在content或typewriterEffect变化时执行
|
|
39
38
|
useEffect(() => {
|
|
39
|
+
// 清除任何现有的定时器
|
|
40
|
+
clearTypeTimer();
|
|
41
|
+
// 如果没有启用打字机效果,直接显示全部内容
|
|
40
42
|
if (!(others === null || others === void 0 ? void 0 : others.typewriterEffect)) {
|
|
41
43
|
setDisplayedContent(content);
|
|
44
|
+
setContentQueue([]);
|
|
42
45
|
return;
|
|
43
46
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
typeTimeoutRef.current = null;
|
|
53
|
-
}
|
|
54
|
-
// 将内容分成两部分:直接显示部分和打字效果部分
|
|
55
|
-
if (content.length <= 20) {
|
|
56
|
-
// 如果内容不超过20个字符,全部使用打字效果
|
|
57
|
-
setDisplayedContent('');
|
|
58
|
-
setContentQueue(content.split(''));
|
|
47
|
+
// 比较传入的content和当前的displayedContent的长度
|
|
48
|
+
const contentLength = content.length;
|
|
49
|
+
const displayedContentLength = displayedContent.length;
|
|
50
|
+
const diffLength = contentLength - displayedContentLength;
|
|
51
|
+
if (diffLength <= 0) {
|
|
52
|
+
// 如果content的长度小于等于displayedContent的长度,则清空contentQueue,直接展示content
|
|
53
|
+
setDisplayedContent(content);
|
|
54
|
+
setContentQueue([]);
|
|
59
55
|
}
|
|
60
56
|
else {
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
const
|
|
57
|
+
// 如果content的长度大于displayedContent的长度,将 content 分为两部分,
|
|
58
|
+
// 前 contentLength - diffLength 个字符直接显示,后 diffLength 个字符使用打字效果
|
|
59
|
+
const displayPart = content.slice(0, contentLength - diffLength);
|
|
60
|
+
const typingPart = content.slice(contentLength - diffLength);
|
|
64
61
|
setDisplayedContent(displayPart);
|
|
65
62
|
setContentQueue(typingPart.split(''));
|
|
66
63
|
}
|
|
67
|
-
// 启动打字机效果
|
|
68
|
-
setTyping(true);
|
|
69
64
|
}, [content, others === null || others === void 0 ? void 0 : others.typewriterEffect]);
|
|
70
|
-
// 执行打字机效果
|
|
65
|
+
// 执行打字机效果 - 仅负责处理contentQueue中的字符
|
|
71
66
|
useEffect(() => {
|
|
72
|
-
|
|
67
|
+
// 如果没有启用打字机效果或没有内容需要打字
|
|
68
|
+
if (!(others === null || others === void 0 ? void 0 : others.typewriterEffect) || contentQueue.length === 0) {
|
|
69
|
+
clearTypeTimer();
|
|
73
70
|
return;
|
|
74
71
|
}
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
// 固定打字速度为50ms一个字
|
|
81
|
-
const timePerChar = 50;
|
|
72
|
+
// 固定吐字速度为50ms一次
|
|
73
|
+
const timePerChar = 100;
|
|
74
|
+
// 动态调整一次吐字的字数,确保1s内吐完,最少吐2个字符
|
|
75
|
+
const charsPerTime = Math.max(Math.floor(contentQueue.length / 10), 2);
|
|
82
76
|
const processNextChar = () => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
clearTimeout(typeTimeoutRef.current);
|
|
86
|
-
typeTimeoutRef.current = null;
|
|
87
|
-
}
|
|
88
|
-
setTyping(false);
|
|
77
|
+
const currentQueue = contentQueue;
|
|
78
|
+
if (currentQueue.length === 0) {
|
|
89
79
|
return;
|
|
90
80
|
}
|
|
91
|
-
//
|
|
92
|
-
const
|
|
93
|
-
const newQueue =
|
|
94
|
-
//
|
|
95
|
-
setDisplayedContent(
|
|
81
|
+
// 获取并移除队列中的第charsPerTime个字符
|
|
82
|
+
const nextChars = currentQueue.slice(0, charsPerTime);
|
|
83
|
+
const newQueue = currentQueue.slice(charsPerTime);
|
|
84
|
+
// 更新显示内容
|
|
85
|
+
setDisplayedContent(prev => prev + nextChars.join(''));
|
|
86
|
+
// 更新队列
|
|
96
87
|
setContentQueue(newQueue);
|
|
97
|
-
// 如果队列还有字符,继续处理下一个
|
|
98
|
-
if (newQueue.length > 0) {
|
|
99
|
-
typeTimeoutRef.current = setTimeout(processNextChar, timePerChar);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
setTyping(false);
|
|
103
|
-
}
|
|
104
88
|
};
|
|
105
89
|
// 开始处理第一个字符
|
|
90
|
+
clearTypeTimer(); // 确保没有并发定时器
|
|
106
91
|
typeTimeoutRef.current = setTimeout(processNextChar, timePerChar);
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
clearTimeout(typeTimeoutRef.current);
|
|
110
|
-
typeTimeoutRef.current = null;
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
}, [others === null || others === void 0 ? void 0 : others.typewriterEffect, typing, contentQueue.length]); // 增加依赖项,确保typing状态变化时会触发
|
|
114
|
-
// 当 typing 状态或 contentQueue 改变时重新启动打字效果
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
if ((others === null || others === void 0 ? void 0 : others.typewriterEffect) && contentQueue.length > 0 && !typing && !typeTimeoutRef.current) {
|
|
117
|
-
setTyping(true);
|
|
118
|
-
}
|
|
119
|
-
}, [contentQueue.length, others === null || others === void 0 ? void 0 : others.typewriterEffect, typing]);
|
|
92
|
+
return clearTypeTimer;
|
|
93
|
+
}, [contentQueue.length, others === null || others === void 0 ? void 0 : others.typewriterEffect]);
|
|
120
94
|
// 处理 HTML 标签
|
|
121
95
|
const processedContent = useMemo(() => {
|
|
122
96
|
// 使用 Record 类型来定义映射关系
|
|
@@ -132,7 +106,7 @@ const Markdown = forwardRef((_a, ref) => {
|
|
|
132
106
|
common_footnote_text_style__font_size: 'span'
|
|
133
107
|
};
|
|
134
108
|
const regex = /<font\s+([^>]+)>([^<]+)<\/font>/g;
|
|
135
|
-
return
|
|
109
|
+
return displayedContent.replace(regex, (match, attributes, text) => {
|
|
136
110
|
let sizeToken = '';
|
|
137
111
|
let colorTokenV2 = '';
|
|
138
112
|
// 提取属性
|
|
@@ -163,7 +137,7 @@ const Markdown = forwardRef((_a, ref) => {
|
|
|
163
137
|
return `<${tag} style="${style}" class="markdownFont">${text}</${tag}>`;
|
|
164
138
|
// return `<span style="${style}">${text}</span>`;
|
|
165
139
|
});
|
|
166
|
-
}, [
|
|
140
|
+
}, [displayedContent]);
|
|
167
141
|
// 转换表情符号
|
|
168
142
|
let transformedContent = useMemo(() => {
|
|
169
143
|
// 这个函数的执行会导致类似于 // 这样的符号被 转成 / 这样的符号
|
package/lib/index.js
CHANGED
|
@@ -68,4 +68,4 @@ var select_1 = require("./select");
|
|
|
68
68
|
Object.defineProperty(exports, "Select", { enumerable: true, get: function () { return tslib_1.__importDefault(select_1).default; } });
|
|
69
69
|
var flip_1 = require("./flip");
|
|
70
70
|
Object.defineProperty(exports, "Flip", { enumerable: true, get: function () { return tslib_1.__importDefault(flip_1).default; } });
|
|
71
|
-
exports.version = '0.3.28-beta.
|
|
71
|
+
exports.version = '0.3.28-beta.7';
|
package/lib/markdown/index.js
CHANGED
|
@@ -29,96 +29,70 @@ const Markdown = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
29
29
|
const [displayedContent, setDisplayedContent] = (0, react_1.useState)(() => (others === null || others === void 0 ? void 0 : others.typewriterEffect) ? '' : content);
|
|
30
30
|
const [contentQueue, setContentQueue] = (0, react_1.useState)(() => (others === null || others === void 0 ? void 0 : others.typewriterEffect) ? content.split('') : []);
|
|
31
31
|
const typeTimeoutRef = (0, react_1.useRef)(null);
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
// 清理定时器函数
|
|
33
|
+
const clearTypeTimer = () => {
|
|
34
|
+
if (typeTimeoutRef.current) {
|
|
35
|
+
clearTimeout(typeTimeoutRef.current);
|
|
36
|
+
typeTimeoutRef.current = null;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// 消息队列更新
|
|
38
|
+
};
|
|
39
|
+
// 消息队列更新 - 仅在content或typewriterEffect变化时执行
|
|
41
40
|
(0, react_1.useEffect)(() => {
|
|
41
|
+
// 清除任何现有的定时器
|
|
42
|
+
clearTypeTimer();
|
|
43
|
+
// 如果没有启用打字机效果,直接显示全部内容
|
|
42
44
|
if (!(others === null || others === void 0 ? void 0 : others.typewriterEffect)) {
|
|
43
45
|
setDisplayedContent(content);
|
|
46
|
+
setContentQueue([]);
|
|
44
47
|
return;
|
|
45
48
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
typeTimeoutRef.current = null;
|
|
55
|
-
}
|
|
56
|
-
// 将内容分成两部分:直接显示部分和打字效果部分
|
|
57
|
-
if (content.length <= 20) {
|
|
58
|
-
// 如果内容不超过20个字符,全部使用打字效果
|
|
59
|
-
setDisplayedContent('');
|
|
60
|
-
setContentQueue(content.split(''));
|
|
49
|
+
// 比较传入的content和当前的displayedContent的长度
|
|
50
|
+
const contentLength = content.length;
|
|
51
|
+
const displayedContentLength = displayedContent.length;
|
|
52
|
+
const diffLength = contentLength - displayedContentLength;
|
|
53
|
+
if (diffLength <= 0) {
|
|
54
|
+
// 如果content的长度小于等于displayedContent的长度,则清空contentQueue,直接展示content
|
|
55
|
+
setDisplayedContent(content);
|
|
56
|
+
setContentQueue([]);
|
|
61
57
|
}
|
|
62
58
|
else {
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
const
|
|
59
|
+
// 如果content的长度大于displayedContent的长度,将 content 分为两部分,
|
|
60
|
+
// 前 contentLength - diffLength 个字符直接显示,后 diffLength 个字符使用打字效果
|
|
61
|
+
const displayPart = content.slice(0, contentLength - diffLength);
|
|
62
|
+
const typingPart = content.slice(contentLength - diffLength);
|
|
66
63
|
setDisplayedContent(displayPart);
|
|
67
64
|
setContentQueue(typingPart.split(''));
|
|
68
65
|
}
|
|
69
|
-
// 启动打字机效果
|
|
70
|
-
setTyping(true);
|
|
71
66
|
}, [content, others === null || others === void 0 ? void 0 : others.typewriterEffect]);
|
|
72
|
-
// 执行打字机效果
|
|
67
|
+
// 执行打字机效果 - 仅负责处理contentQueue中的字符
|
|
73
68
|
(0, react_1.useEffect)(() => {
|
|
74
|
-
|
|
69
|
+
// 如果没有启用打字机效果或没有内容需要打字
|
|
70
|
+
if (!(others === null || others === void 0 ? void 0 : others.typewriterEffect) || contentQueue.length === 0) {
|
|
71
|
+
clearTypeTimer();
|
|
75
72
|
return;
|
|
76
73
|
}
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
// 固定打字速度为50ms一个字
|
|
83
|
-
const timePerChar = 50;
|
|
74
|
+
// 固定吐字速度为50ms一次
|
|
75
|
+
const timePerChar = 100;
|
|
76
|
+
// 动态调整一次吐字的字数,确保1s内吐完,最少吐2个字符
|
|
77
|
+
const charsPerTime = Math.max(Math.floor(contentQueue.length / 10), 2);
|
|
84
78
|
const processNextChar = () => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
clearTimeout(typeTimeoutRef.current);
|
|
88
|
-
typeTimeoutRef.current = null;
|
|
89
|
-
}
|
|
90
|
-
setTyping(false);
|
|
79
|
+
const currentQueue = contentQueue;
|
|
80
|
+
if (currentQueue.length === 0) {
|
|
91
81
|
return;
|
|
92
82
|
}
|
|
93
|
-
//
|
|
94
|
-
const
|
|
95
|
-
const newQueue =
|
|
96
|
-
//
|
|
97
|
-
setDisplayedContent(
|
|
83
|
+
// 获取并移除队列中的第charsPerTime个字符
|
|
84
|
+
const nextChars = currentQueue.slice(0, charsPerTime);
|
|
85
|
+
const newQueue = currentQueue.slice(charsPerTime);
|
|
86
|
+
// 更新显示内容
|
|
87
|
+
setDisplayedContent(prev => prev + nextChars.join(''));
|
|
88
|
+
// 更新队列
|
|
98
89
|
setContentQueue(newQueue);
|
|
99
|
-
// 如果队列还有字符,继续处理下一个
|
|
100
|
-
if (newQueue.length > 0) {
|
|
101
|
-
typeTimeoutRef.current = setTimeout(processNextChar, timePerChar);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
setTyping(false);
|
|
105
|
-
}
|
|
106
90
|
};
|
|
107
91
|
// 开始处理第一个字符
|
|
92
|
+
clearTypeTimer(); // 确保没有并发定时器
|
|
108
93
|
typeTimeoutRef.current = setTimeout(processNextChar, timePerChar);
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
clearTimeout(typeTimeoutRef.current);
|
|
112
|
-
typeTimeoutRef.current = null;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
}, [others === null || others === void 0 ? void 0 : others.typewriterEffect, typing, contentQueue.length]); // 增加依赖项,确保typing状态变化时会触发
|
|
116
|
-
// 当 typing 状态或 contentQueue 改变时重新启动打字效果
|
|
117
|
-
(0, react_1.useEffect)(() => {
|
|
118
|
-
if ((others === null || others === void 0 ? void 0 : others.typewriterEffect) && contentQueue.length > 0 && !typing && !typeTimeoutRef.current) {
|
|
119
|
-
setTyping(true);
|
|
120
|
-
}
|
|
121
|
-
}, [contentQueue.length, others === null || others === void 0 ? void 0 : others.typewriterEffect, typing]);
|
|
94
|
+
return clearTypeTimer;
|
|
95
|
+
}, [contentQueue.length, others === null || others === void 0 ? void 0 : others.typewriterEffect]);
|
|
122
96
|
// 处理 HTML 标签
|
|
123
97
|
const processedContent = (0, react_1.useMemo)(() => {
|
|
124
98
|
// 使用 Record 类型来定义映射关系
|
|
@@ -134,7 +108,7 @@ const Markdown = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
134
108
|
common_footnote_text_style__font_size: 'span'
|
|
135
109
|
};
|
|
136
110
|
const regex = /<font\s+([^>]+)>([^<]+)<\/font>/g;
|
|
137
|
-
return
|
|
111
|
+
return displayedContent.replace(regex, (match, attributes, text) => {
|
|
138
112
|
let sizeToken = '';
|
|
139
113
|
let colorTokenV2 = '';
|
|
140
114
|
// 提取属性
|
|
@@ -165,7 +139,7 @@ const Markdown = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
165
139
|
return `<${tag} style="${style}" class="markdownFont">${text}</${tag}>`;
|
|
166
140
|
// return `<span style="${style}">${text}</span>`;
|
|
167
141
|
});
|
|
168
|
-
}, [
|
|
142
|
+
}, [displayedContent]);
|
|
169
143
|
// 转换表情符号
|
|
170
144
|
let transformedContent = (0, react_1.useMemo)(() => {
|
|
171
145
|
// 这个函数的执行会导致类似于 // 这样的符号被 转成 / 这样的符号
|