@ant-design/agentic-ui 2.30.3 → 2.30.5
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/Bubble/AIBubble.js +15 -5
- package/dist/Bubble/List/PureBubbleList.js +16 -4
- package/dist/Bubble/List/index.js +18 -4
- package/dist/MarkdownRenderer/AnimationText.d.ts +2 -1
- package/dist/MarkdownRenderer/AnimationText.js +24 -109
- package/dist/MarkdownRenderer/CharacterQueue.js +9 -1
- package/dist/MarkdownRenderer/MarkdownRenderer.js +11 -3
- package/dist/MarkdownRenderer/types.d.ts +6 -0
- package/dist/MarkdownRenderer/useMarkdownToReact.js +62 -33
- package/package.json +1 -1
package/dist/Bubble/AIBubble.js
CHANGED
|
@@ -212,9 +212,10 @@ function _ts_generator(thisArg, body) {
|
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
|
-
import { memo, useContext } from "react";
|
|
215
|
+
import { memo, useContext, useRef } from "react";
|
|
216
216
|
import { ConfigProvider, Flex } from "antd";
|
|
217
217
|
import clsx from "clsx";
|
|
218
|
+
import { nanoid } from "nanoid";
|
|
218
219
|
import React from "react";
|
|
219
220
|
import { BubbleAvatar } from "./Avatar";
|
|
220
221
|
import { BubbleBeforeNode } from "./BubbleBeforeNode";
|
|
@@ -222,6 +223,7 @@ import { BubbleConfigContext } from "./BubbleConfigProvide";
|
|
|
222
223
|
import { BubbleFileView } from "./FileView";
|
|
223
224
|
import { BubbleMessageDisplay } from "./MessagesContent";
|
|
224
225
|
import { MessagesContext } from "./MessagesContent/BubbleContext";
|
|
226
|
+
import { LOADING_FLAT } from "./MessagesContent";
|
|
225
227
|
import { BubbleExtra } from "./MessagesContent/BubbleExtra";
|
|
226
228
|
import { useStyle } from "./style";
|
|
227
229
|
import { BubbleTitle } from "./Title";
|
|
@@ -269,13 +271,14 @@ var getTaskList = function getTaskList(originData) {
|
|
|
269
271
|
* </AIBubble>
|
|
270
272
|
* ```
|
|
271
273
|
*/ export var AIBubble = /*#__PURE__*/ memo(function(props) {
|
|
272
|
-
var _props_readonly;
|
|
274
|
+
var _ref, _messageDisplayKeyRef_current, _props_readonly;
|
|
273
275
|
var _props_originData, _props_originData1, _props_originData2, _props_originData3, _props_originData4, _props_originData5, _props_bubbleRenderConfig, _props_originData_fileMap, _props_originData6, _props_bubbleRenderConfig1, _props_styles, _props_bubbleRenderConfig2, _bubbleRenderConfig_render;
|
|
274
276
|
var onAvatarClick = props.onAvatarClick, className = props.className, style = props.style, bubbleRenderConfig = props.bubbleRenderConfig, classNames = props.classNames, styles = props.styles, originData = props.originData, preMessage = props.preMessage;
|
|
275
277
|
var _React_useState = _sliced_to_array(React.useState(false), 2), hidePadding = _React_useState[0], setHidePadding = _React_useState[1];
|
|
278
|
+
var messageDisplayKeyRef = useRef(null);
|
|
276
279
|
var getPrefixCls = useContext(ConfigProvider.ConfigContext).getPrefixCls;
|
|
277
280
|
var context = useContext(BubbleConfigContext);
|
|
278
|
-
var
|
|
281
|
+
var _ref1 = context || {}, compact = _ref1.compact, standalone = _ref1.standalone, extraShowOnHover = _ref1.extraShowOnHover;
|
|
279
282
|
var prefixClass = getPrefixCls('agentic');
|
|
280
283
|
var _useStyle = useStyle(prefixClass), wrapSSR = _useStyle.wrapSSR, hashId = _useStyle.hashId;
|
|
281
284
|
var preMessageSameRole = isSameRoleAsPrevious(preMessage, originData);
|
|
@@ -300,14 +303,21 @@ var getTaskList = function getTaskList(originData) {
|
|
|
300
303
|
prefixCls: "".concat(prefixClass, "-bubble-avatar"),
|
|
301
304
|
style: styles === null || styles === void 0 ? void 0 : styles.bubbleListItemAvatarStyle
|
|
302
305
|
}));
|
|
306
|
+
var id = props === null || props === void 0 ? void 0 : (_props_originData = props.originData) === null || _props_originData === void 0 ? void 0 : _props_originData.id;
|
|
307
|
+
if (id === undefined || id === LOADING_FLAT) {
|
|
308
|
+
if (!messageDisplayKeyRef.current) {
|
|
309
|
+
messageDisplayKeyRef.current = nanoid();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
var messageDisplayKey = (_ref = (_messageDisplayKeyRef_current = messageDisplayKeyRef.current) !== null && _messageDisplayKeyRef_current !== void 0 ? _messageDisplayKeyRef_current : id) !== null && _ref !== void 0 ? _ref : nanoid();
|
|
303
313
|
var messageContent = /*#__PURE__*/ React.createElement(BubbleMessageDisplay, {
|
|
304
314
|
markdownRenderConfig: props.markdownRenderConfig,
|
|
305
315
|
docListProps: props.docListProps,
|
|
306
316
|
bubbleListRef: props.bubbleListRef,
|
|
307
317
|
bubbleListItemExtraStyle: styles === null || styles === void 0 ? void 0 : styles.bubbleListItemExtraStyle,
|
|
308
318
|
bubbleRef: props.bubbleRef,
|
|
309
|
-
content: props === null || props === void 0 ? void 0 : (
|
|
310
|
-
key:
|
|
319
|
+
content: props === null || props === void 0 ? void 0 : (_props_originData1 = props.originData) === null || _props_originData1 === void 0 ? void 0 : _props_originData1.content,
|
|
320
|
+
key: messageDisplayKey,
|
|
311
321
|
"data-id": props === null || props === void 0 ? void 0 : (_props_originData2 = props.originData) === null || _props_originData2 === void 0 ? void 0 : _props_originData2.id,
|
|
312
322
|
avatar: props === null || props === void 0 ? void 0 : (_props_originData3 = props.originData) === null || _props_originData3 === void 0 ? void 0 : _props_originData3.meta,
|
|
313
323
|
readonly: (_props_readonly = props.readonly) !== null && _props_readonly !== void 0 ? _props_readonly : false,
|
|
@@ -74,8 +74,9 @@ export var PureBubbleList = /*#__PURE__*/ React.memo(function(props) {
|
|
|
74
74
|
}, [
|
|
75
75
|
props.style
|
|
76
76
|
]);
|
|
77
|
-
// 为 loading 项生成唯一的 key,使用 ref 缓存以确保稳定性
|
|
78
77
|
var loadingKeysRef = useRef(new Map());
|
|
78
|
+
var loadingKeyByIndexRef = useRef(new Map());
|
|
79
|
+
var realIdToStableKeyRef = useRef(new Map());
|
|
79
80
|
var listDom = useMemo(function() {
|
|
80
81
|
var _props_lazy;
|
|
81
82
|
var isLazyEnabled = (_props_lazy = props.lazy) === null || _props_lazy === void 0 ? void 0 : _props_lazy.enable;
|
|
@@ -88,15 +89,26 @@ export var PureBubbleList = /*#__PURE__*/ React.memo(function(props) {
|
|
|
88
89
|
isLatest: isLast,
|
|
89
90
|
isLast: isLast
|
|
90
91
|
});
|
|
91
|
-
|
|
92
|
-
// 使用 index 和 createAt 的组合作为缓存 key,确保同一项在重新渲染时保持相同的 key
|
|
93
|
-
var itemKey = item.id;
|
|
92
|
+
var itemKey;
|
|
94
93
|
if (item.id === LOADING_FLAT) {
|
|
95
94
|
var cacheKey = "".concat(index, "-").concat(item.createAt || Date.now());
|
|
96
95
|
if (!loadingKeysRef.current.has(cacheKey)) {
|
|
97
96
|
loadingKeysRef.current.set(cacheKey, nanoid());
|
|
98
97
|
}
|
|
99
98
|
itemKey = loadingKeysRef.current.get(cacheKey);
|
|
99
|
+
loadingKeyByIndexRef.current.set(index, itemKey);
|
|
100
|
+
} else {
|
|
101
|
+
var realId = item.id;
|
|
102
|
+
var prevLoadingKey = loadingKeyByIndexRef.current.get(index);
|
|
103
|
+
if (prevLoadingKey) {
|
|
104
|
+
itemKey = prevLoadingKey;
|
|
105
|
+
realIdToStableKeyRef.current.set(realId, prevLoadingKey);
|
|
106
|
+
loadingKeyByIndexRef.current.delete(index);
|
|
107
|
+
} else if (realIdToStableKeyRef.current.has(realId)) {
|
|
108
|
+
itemKey = realIdToStableKeyRef.current.get(realId);
|
|
109
|
+
} else {
|
|
110
|
+
itemKey = realId;
|
|
111
|
+
}
|
|
100
112
|
}
|
|
101
113
|
var bubbleElement = /*#__PURE__*/ React.createElement(BubbleComponent, {
|
|
102
114
|
key: itemKey,
|
|
@@ -161,8 +161,11 @@ import { useStyle } from "./style";
|
|
|
161
161
|
JSON.stringify(props.style)
|
|
162
162
|
]);
|
|
163
163
|
// 为 loading 项生成唯一的 key,使用 ref 缓存以确保稳定性
|
|
164
|
-
// 使用 item 的唯一标识(index + createAt)作为缓存 key
|
|
165
164
|
var loadingKeysRef = useRef(new Map());
|
|
165
|
+
// 记录每个 index 在上一轮是否是 loading,用于 loading→real 过渡时保持 key 稳定,避免闪动
|
|
166
|
+
var loadingKeyByIndexRef = useRef(new Map());
|
|
167
|
+
// 真实 id 映射到稳定 key(过渡后沿用),避免同一条消息因 id 变化导致 remount
|
|
168
|
+
var realIdToStableKeyRef = useRef(new Map());
|
|
166
169
|
var bubbleListDom = useMemo(function() {
|
|
167
170
|
var _props_lazy;
|
|
168
171
|
var isLazyEnabled = (_props_lazy = props.lazy) === null || _props_lazy === void 0 ? void 0 : _props_lazy.enable;
|
|
@@ -174,15 +177,26 @@ import { useStyle } from "./style";
|
|
|
174
177
|
// 保持向后兼容性,设置isLatest
|
|
175
178
|
item.isLatest = isLast;
|
|
176
179
|
item.isLast = isLast;
|
|
177
|
-
|
|
178
|
-
// 使用 index 和 createAt 的组合作为缓存 key,确保同一项在重新渲染时保持相同的 key
|
|
179
|
-
var itemKey = item.id;
|
|
180
|
+
var itemKey;
|
|
180
181
|
if (item.id === LOADING_FLAT) {
|
|
181
182
|
var cacheKey = "".concat(index, "-").concat(item.createAt || Date.now());
|
|
182
183
|
if (!loadingKeysRef.current.has(cacheKey)) {
|
|
183
184
|
loadingKeysRef.current.set(cacheKey, nanoid());
|
|
184
185
|
}
|
|
185
186
|
itemKey = loadingKeysRef.current.get(cacheKey);
|
|
187
|
+
loadingKeyByIndexRef.current.set(index, itemKey);
|
|
188
|
+
} else {
|
|
189
|
+
var realId = item.id;
|
|
190
|
+
var prevLoadingKey = loadingKeyByIndexRef.current.get(index);
|
|
191
|
+
if (prevLoadingKey) {
|
|
192
|
+
itemKey = prevLoadingKey;
|
|
193
|
+
realIdToStableKeyRef.current.set(realId, prevLoadingKey);
|
|
194
|
+
loadingKeyByIndexRef.current.delete(index);
|
|
195
|
+
} else if (realIdToStableKeyRef.current.has(realId)) {
|
|
196
|
+
itemKey = realIdToStableKeyRef.current.get(realId);
|
|
197
|
+
} else {
|
|
198
|
+
itemKey = realId;
|
|
199
|
+
}
|
|
186
200
|
}
|
|
187
201
|
var bubbleElement = /*#__PURE__*/ React.createElement(Bubble, {
|
|
188
202
|
key: itemKey,
|
|
@@ -13,7 +13,8 @@ export interface AnimationTextProps {
|
|
|
13
13
|
* 流式文字淡入动画组件。
|
|
14
14
|
*
|
|
15
15
|
* 采用 opacity + translateY(GPU 硬件加速),清爽流派。
|
|
16
|
-
*
|
|
16
|
+
* 同一段流式前缀追加只触发**一次**入场动画;后续增量仅更新文案。内容被替换
|
|
17
|
+
* (非前缀增长)时重新播放入场。动画结束后仍用 span 包裹以保持布局稳定。
|
|
17
18
|
*/
|
|
18
19
|
declare const AnimationText: React.NamedExoticComponent<AnimationTextProps>;
|
|
19
20
|
export default AnimationText;
|
|
@@ -6,25 +6,6 @@ function _array_like_to_array(arr, len) {
|
|
|
6
6
|
function _array_with_holes(arr) {
|
|
7
7
|
if (Array.isArray(arr)) return arr;
|
|
8
8
|
}
|
|
9
|
-
function _array_without_holes(arr) {
|
|
10
|
-
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
11
|
-
}
|
|
12
|
-
function _define_property(obj, key, value) {
|
|
13
|
-
if (key in obj) {
|
|
14
|
-
Object.defineProperty(obj, key, {
|
|
15
|
-
value: value,
|
|
16
|
-
enumerable: true,
|
|
17
|
-
configurable: true,
|
|
18
|
-
writable: true
|
|
19
|
-
});
|
|
20
|
-
} else {
|
|
21
|
-
obj[key] = value;
|
|
22
|
-
}
|
|
23
|
-
return obj;
|
|
24
|
-
}
|
|
25
|
-
function _iterable_to_array(iter) {
|
|
26
|
-
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
27
|
-
}
|
|
28
9
|
function _iterable_to_array_limit(arr, i) {
|
|
29
10
|
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
30
11
|
if (_i == null) return;
|
|
@@ -52,54 +33,9 @@ function _iterable_to_array_limit(arr, i) {
|
|
|
52
33
|
function _non_iterable_rest() {
|
|
53
34
|
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
54
35
|
}
|
|
55
|
-
function _non_iterable_spread() {
|
|
56
|
-
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
57
|
-
}
|
|
58
|
-
function _object_spread(target) {
|
|
59
|
-
for(var i = 1; i < arguments.length; i++){
|
|
60
|
-
var source = arguments[i] != null ? arguments[i] : {};
|
|
61
|
-
var ownKeys = Object.keys(source);
|
|
62
|
-
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
63
|
-
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
64
|
-
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
ownKeys.forEach(function(key) {
|
|
68
|
-
_define_property(target, key, source[key]);
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
return target;
|
|
72
|
-
}
|
|
73
|
-
function ownKeys(object, enumerableOnly) {
|
|
74
|
-
var keys = Object.keys(object);
|
|
75
|
-
if (Object.getOwnPropertySymbols) {
|
|
76
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
77
|
-
if (enumerableOnly) {
|
|
78
|
-
symbols = symbols.filter(function(sym) {
|
|
79
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
keys.push.apply(keys, symbols);
|
|
83
|
-
}
|
|
84
|
-
return keys;
|
|
85
|
-
}
|
|
86
|
-
function _object_spread_props(target, source) {
|
|
87
|
-
source = source != null ? source : {};
|
|
88
|
-
if (Object.getOwnPropertyDescriptors) {
|
|
89
|
-
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
90
|
-
} else {
|
|
91
|
-
ownKeys(Object(source)).forEach(function(key) {
|
|
92
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
return target;
|
|
96
|
-
}
|
|
97
36
|
function _sliced_to_array(arr, i) {
|
|
98
37
|
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
99
38
|
}
|
|
100
|
-
function _to_consumable_array(arr) {
|
|
101
|
-
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
102
|
-
}
|
|
103
39
|
function _unsupported_iterable_to_array(o, minLen) {
|
|
104
40
|
if (!o) return;
|
|
105
41
|
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
@@ -134,11 +70,13 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
134
70
|
* 流式文字淡入动画组件。
|
|
135
71
|
*
|
|
136
72
|
* 采用 opacity + translateY(GPU 硬件加速),清爽流派。
|
|
137
|
-
*
|
|
73
|
+
* 同一段流式前缀追加只触发**一次**入场动画;后续增量仅更新文案。内容被替换
|
|
74
|
+
* (非前缀增长)时重新播放入场。动画结束后仍用 span 包裹以保持布局稳定。
|
|
138
75
|
*/ var AnimationText = /*#__PURE__*/ React.memo(function(param) {
|
|
139
76
|
var children = param.children, animationConfig = param.animationConfig;
|
|
140
77
|
var _ref = animationConfig || {}, _ref_fadeDuration = _ref.fadeDuration, fadeDuration = _ref_fadeDuration === void 0 ? 200 : _ref_fadeDuration, _ref_easing = _ref.easing, easing = _ref_easing === void 0 ? 'ease-out' : _ref_easing;
|
|
141
|
-
var _useState = _sliced_to_array(useState(
|
|
78
|
+
var _useState = _sliced_to_array(useState(false), 2), animComplete = _useState[0], setAnimComplete = _useState[1];
|
|
79
|
+
var _useState1 = _sliced_to_array(useState(0), 2), animSession = _useState1[0], setAnimSession = _useState1[1];
|
|
142
80
|
var prevTextRef = useRef('');
|
|
143
81
|
var text = extractText(children);
|
|
144
82
|
var hasElementContent = hasElementNode(children);
|
|
@@ -148,44 +86,28 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
148
86
|
return;
|
|
149
87
|
}
|
|
150
88
|
if (text === prevTextRef.current) return;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
key: '0',
|
|
155
|
-
text: text,
|
|
156
|
-
content: children,
|
|
157
|
-
done: false
|
|
158
|
-
}
|
|
159
|
-
]);
|
|
89
|
+
var prev = prevTextRef.current;
|
|
90
|
+
if (!prev) {
|
|
91
|
+
if (!text) return;
|
|
160
92
|
prevTextRef.current = text;
|
|
93
|
+
setAnimComplete(false);
|
|
161
94
|
return;
|
|
162
95
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
text: newText,
|
|
171
|
-
done: false
|
|
172
|
-
}
|
|
173
|
-
]);
|
|
96
|
+
if (text.length > prev.length && text.startsWith(prev)) {
|
|
97
|
+
prevTextRef.current = text;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
setAnimComplete(false);
|
|
101
|
+
setAnimSession(function(s) {
|
|
102
|
+
return s + 1;
|
|
174
103
|
});
|
|
175
104
|
prevTextRef.current = text;
|
|
176
105
|
}, [
|
|
177
106
|
text,
|
|
178
|
-
children,
|
|
179
107
|
hasElementContent
|
|
180
108
|
]);
|
|
181
|
-
var handleAnimationEnd = function handleAnimationEnd(
|
|
182
|
-
|
|
183
|
-
return prev.map(function(c) {
|
|
184
|
-
return c.key === key ? _object_spread_props(_object_spread({}, c), {
|
|
185
|
-
done: true
|
|
186
|
-
}) : c;
|
|
187
|
-
});
|
|
188
|
-
});
|
|
109
|
+
var handleAnimationEnd = function handleAnimationEnd() {
|
|
110
|
+
return setAnimComplete(true);
|
|
189
111
|
};
|
|
190
112
|
var animationStyle = useMemo(function() {
|
|
191
113
|
return {
|
|
@@ -207,20 +129,13 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
207
129
|
if (hasElementContent) {
|
|
208
130
|
return /*#__PURE__*/ React.createElement(React.Fragment, null, children);
|
|
209
131
|
}
|
|
210
|
-
return /*#__PURE__*/ React.createElement(
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
key: chunk.key,
|
|
218
|
-
style: animationStyle,
|
|
219
|
-
onAnimationEnd: function onAnimationEnd() {
|
|
220
|
-
return handleAnimationEnd(chunk.key);
|
|
221
|
-
}
|
|
222
|
-
}, rendered);
|
|
223
|
-
}));
|
|
132
|
+
return animComplete ? /*#__PURE__*/ React.createElement("span", {
|
|
133
|
+
style: doneChunkStyle
|
|
134
|
+
}, children) : /*#__PURE__*/ React.createElement("span", {
|
|
135
|
+
key: animSession,
|
|
136
|
+
style: animationStyle,
|
|
137
|
+
onAnimationEnd: handleAnimationEnd
|
|
138
|
+
}, children);
|
|
224
139
|
});
|
|
225
140
|
AnimationText.displayName = 'AnimationText';
|
|
226
141
|
export default AnimationText;
|
|
@@ -77,7 +77,8 @@ var DEFAULT_BACKGROUND_BATCH_MULTIPLIER = 10;
|
|
|
77
77
|
speed: (_ref2 = options === null || options === void 0 ? void 0 : options.speed) !== null && _ref2 !== void 0 ? _ref2 : DEFAULT_SPEED,
|
|
78
78
|
flushOnComplete: (_ref3 = options === null || options === void 0 ? void 0 : options.flushOnComplete) !== null && _ref3 !== void 0 ? _ref3 : true,
|
|
79
79
|
backgroundInterval: (_ref4 = options === null || options === void 0 ? void 0 : options.backgroundInterval) !== null && _ref4 !== void 0 ? _ref4 : DEFAULT_BACKGROUND_INTERVAL,
|
|
80
|
-
backgroundBatchMultiplier: (_ref5 = options === null || options === void 0 ? void 0 : options.backgroundBatchMultiplier) !== null && _ref5 !== void 0 ? _ref5 : DEFAULT_BACKGROUND_BATCH_MULTIPLIER
|
|
80
|
+
backgroundBatchMultiplier: (_ref5 = options === null || options === void 0 ? void 0 : options.backgroundBatchMultiplier) !== null && _ref5 !== void 0 ? _ref5 : DEFAULT_BACKGROUND_BATCH_MULTIPLIER,
|
|
81
|
+
animateTailChars: options === null || options === void 0 ? void 0 : options.animateTailChars
|
|
81
82
|
};
|
|
82
83
|
this.handleVisibilityChange = this.handleVisibilityChange.bind(this);
|
|
83
84
|
if (typeof document !== 'undefined') {
|
|
@@ -94,6 +95,13 @@ var DEFAULT_BACKGROUND_BATCH_MULTIPLIER = 10;
|
|
|
94
95
|
this.onFlush(content);
|
|
95
96
|
return;
|
|
96
97
|
}
|
|
98
|
+
// 仅对末尾 N 字做动画:立即展示前面内容
|
|
99
|
+
var tail = this.options.animateTailChars;
|
|
100
|
+
if (tail !== undefined && tail > 0 && content.length > tail) {
|
|
101
|
+
var staticLength = content.length - tail;
|
|
102
|
+
this.displayedLength = Math.max(this.displayedLength, staticLength);
|
|
103
|
+
this.onFlush(content.slice(0, this.displayedLength));
|
|
104
|
+
}
|
|
97
105
|
this.ensureTicking();
|
|
98
106
|
}
|
|
99
107
|
},
|
|
@@ -263,7 +263,15 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
263
263
|
}, [
|
|
264
264
|
plugins
|
|
265
265
|
]);
|
|
266
|
-
//
|
|
266
|
+
// 字符队列管理:流式时仅对最后 50 字做动画,避免每段逐字输出
|
|
267
|
+
var resolvedQueueOptions = useMemo(function() {
|
|
268
|
+
return streaming ? _object_spread({
|
|
269
|
+
animateTailChars: 50
|
|
270
|
+
}, queueOptions) : queueOptions;
|
|
271
|
+
}, [
|
|
272
|
+
streaming,
|
|
273
|
+
queueOptions
|
|
274
|
+
]);
|
|
267
275
|
useEffect(function() {
|
|
268
276
|
if (!streaming) {
|
|
269
277
|
// 非流式:直接展示全部内容
|
|
@@ -273,14 +281,14 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
273
281
|
if (!queueRef.current) {
|
|
274
282
|
queueRef.current = new CharacterQueue(function(displayed) {
|
|
275
283
|
return setDisplayedContent(displayed);
|
|
276
|
-
},
|
|
284
|
+
}, resolvedQueueOptions);
|
|
277
285
|
}
|
|
278
286
|
queueRef.current.push(content || '');
|
|
279
287
|
return undefined;
|
|
280
288
|
}, [
|
|
281
289
|
content,
|
|
282
290
|
streaming,
|
|
283
|
-
|
|
291
|
+
resolvedQueueOptions
|
|
284
292
|
]);
|
|
285
293
|
// 流式完成时 flush 所有剩余内容
|
|
286
294
|
useEffect(function() {
|
|
@@ -6,6 +6,12 @@ export interface CharacterQueueOptions {
|
|
|
6
6
|
charsPerFrame?: number;
|
|
7
7
|
/** 是否启用打字动画,默认 true(流式时) */
|
|
8
8
|
animate?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 仅对末尾 N 个字符做动画,前面内容立即展示。
|
|
11
|
+
* 设为 50 时,每次 push 只对最后 50 字逐字输出,避免整段逐字动画。
|
|
12
|
+
* 默认 undefined 表示整段动画(原有行为)。
|
|
13
|
+
*/
|
|
14
|
+
animateTailChars?: number;
|
|
9
15
|
/** 动画速度因子,1.0 为标准速度 */
|
|
10
16
|
speed?: number;
|
|
11
17
|
/** 内容完成后立即 flush 全部剩余 */
|
|
@@ -1079,45 +1079,62 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
1079
1079
|
return null;
|
|
1080
1080
|
}
|
|
1081
1081
|
};
|
|
1082
|
+
/** 流式未闭合围栏内,空行后的下一行是否仍像围栏内代码(JSON 等)延续 */ var lineLooksLikeFenceCodeContinuation = function lineLooksLikeFenceCodeContinuation(rawLine) {
|
|
1083
|
+
var t = rawLine.trimStart();
|
|
1084
|
+
if (!t) {
|
|
1085
|
+
return true;
|
|
1086
|
+
}
|
|
1087
|
+
if (t.startsWith('```') || t.startsWith('~~~')) {
|
|
1088
|
+
return true;
|
|
1089
|
+
}
|
|
1090
|
+
var c = t[0];
|
|
1091
|
+
return c === '{' || c === '}' || c === '[' || c === ']' || c === '"' || c === "'" || c === '`' || c === '-' || c === ',' || c >= '0' && c <= '9';
|
|
1092
|
+
};
|
|
1082
1093
|
/**
|
|
1083
1094
|
* 将 markdown 按块(双换行)拆分,尊重代码围栏边界。
|
|
1084
1095
|
* 返回的每个块是一个独立的 markdown 片段,可单独解析。
|
|
1085
|
-
|
|
1096
|
+
*
|
|
1097
|
+
* 流式且围栏未闭合时,inFence 会一直为 true,段间 `\n\n` 在按行拆分时只出现一个空行,
|
|
1098
|
+
* 无法用「连续两个空行」检测。此时在空行处前瞻下一行:若不像围栏内代码延续则虚拟闭合围栏,
|
|
1099
|
+
* 使末块能变为非末块且缓存键与先前一致。
|
|
1100
|
+
*/ var splitMarkdownBlocks = function splitMarkdownBlocks(content, streaming) {
|
|
1086
1101
|
var lines = content.split('\n');
|
|
1087
1102
|
var blocks = [];
|
|
1088
1103
|
var current = [];
|
|
1089
1104
|
var inFence = false;
|
|
1090
|
-
var
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1105
|
+
for(var i = 0; i < lines.length; i += 1){
|
|
1106
|
+
var line = lines[i];
|
|
1107
|
+
var trimmed = line.trimStart();
|
|
1108
|
+
if (trimmed.startsWith('```') || trimmed.startsWith('~~~')) {
|
|
1109
|
+
inFence = !inFence;
|
|
1110
|
+
}
|
|
1111
|
+
if (streaming && inFence && line === '' && current.length > 0) {
|
|
1112
|
+
var prev = current[current.length - 1];
|
|
1113
|
+
var nextLine = lines[i + 1];
|
|
1114
|
+
var doubleBlankInsideFence = prev === '';
|
|
1115
|
+
var blankThenNonCodeLine = prev !== '' && nextLine !== undefined && nextLine.trim() !== '' && !lineLooksLikeFenceCodeContinuation(nextLine);
|
|
1116
|
+
if (doubleBlankInsideFence || blankThenNonCodeLine) {
|
|
1117
|
+
var body = current;
|
|
1118
|
+
while(body.length > 0 && body[body.length - 1] === ''){
|
|
1119
|
+
body = body.slice(0, -1);
|
|
1120
|
+
}
|
|
1121
|
+
if (body.length > 0) {
|
|
1122
|
+
blocks.push(body.join('\n'));
|
|
1104
1123
|
}
|
|
1124
|
+
current = [];
|
|
1125
|
+
inFence = false;
|
|
1126
|
+
continue;
|
|
1105
1127
|
}
|
|
1106
|
-
current.push(line);
|
|
1107
1128
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
_iterator.return();
|
|
1115
|
-
}
|
|
1116
|
-
} finally{
|
|
1117
|
-
if (_didIteratorError) {
|
|
1118
|
-
throw _iteratorError;
|
|
1129
|
+
if (!inFence && line === '' && current.length > 0) {
|
|
1130
|
+
var prev1 = current[current.length - 1];
|
|
1131
|
+
if (prev1 === '') {
|
|
1132
|
+
blocks.push(current.join('\n'));
|
|
1133
|
+
current = [];
|
|
1134
|
+
continue;
|
|
1119
1135
|
}
|
|
1120
1136
|
}
|
|
1137
|
+
current.push(line);
|
|
1121
1138
|
}
|
|
1122
1139
|
if (current.length > 0) {
|
|
1123
1140
|
blocks.push(current.join('\n'));
|
|
@@ -1152,6 +1169,7 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1152
1169
|
var processorRef = useRef(null);
|
|
1153
1170
|
var blockCacheRef = useRef(new Map());
|
|
1154
1171
|
var lastBlockRef = useRef(null);
|
|
1172
|
+
var prevContentRef = useRef('');
|
|
1155
1173
|
var processor = useMemo(function() {
|
|
1156
1174
|
var p = createHastProcessor(options === null || options === void 0 ? void 0 : options.remarkPlugins, options === null || options === void 0 ? void 0 : options.htmlConfig);
|
|
1157
1175
|
processorRef.current = p;
|
|
@@ -1171,22 +1189,33 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1171
1189
|
options === null || options === void 0 ? void 0 : options.linkConfig
|
|
1172
1190
|
]);
|
|
1173
1191
|
return useMemo(function() {
|
|
1174
|
-
if (!content)
|
|
1192
|
+
if (!content) {
|
|
1193
|
+
prevContentRef.current = '';
|
|
1194
|
+
return null;
|
|
1195
|
+
}
|
|
1196
|
+
var prevContent = prevContentRef.current;
|
|
1197
|
+
if (prevContent && content !== prevContent && !content.startsWith(prevContent)) {
|
|
1198
|
+
blockCacheRef.current = new Map();
|
|
1199
|
+
lastBlockRef.current = null;
|
|
1200
|
+
}
|
|
1201
|
+
prevContentRef.current = content;
|
|
1175
1202
|
try {
|
|
1176
1203
|
var preprocessed = content.replace(new RegExp(JINJA_DOLLAR_PLACEHOLDER, 'g'), '$');
|
|
1177
|
-
var blocks = splitMarkdownBlocks(preprocessed);
|
|
1204
|
+
var blocks = splitMarkdownBlocks(preprocessed, options === null || options === void 0 ? void 0 : options.streaming);
|
|
1178
1205
|
if (blocks.length === 0) return null;
|
|
1179
1206
|
var cache = blockCacheRef.current;
|
|
1180
1207
|
var newCache = new Map();
|
|
1181
1208
|
var elements = [];
|
|
1209
|
+
var KEY_PREFIX_LEN = 64;
|
|
1182
1210
|
for(var i = 0; i < blocks.length; i++){
|
|
1183
1211
|
var block = blocks[i];
|
|
1184
1212
|
var isLast = i === blocks.length - 1;
|
|
1185
1213
|
// 用 index + 内容前 64 字符作 key,保持稳定性:
|
|
1186
|
-
//
|
|
1187
|
-
//
|
|
1188
|
-
//
|
|
1189
|
-
var
|
|
1214
|
+
// - 末块在流式中节流时,用 lastBlockRef.source 的切片作 key,避免每次追加字符导致 key 变化
|
|
1215
|
+
// - 末块变为非末块时,必须与先前 key 一致,否则 ChartBlockRenderer 等会 unmount/remount 闪烁
|
|
1216
|
+
// - 因此统一用「实际展示内容」的 slice 作 key,节流时用 lastBlockRef.source
|
|
1217
|
+
var contentForKey = isLast && (options === null || options === void 0 ? void 0 : options.streaming) && lastBlockRef.current && !shouldReparseLastBlock(lastBlockRef.current.source, block, options === null || options === void 0 ? void 0 : options.streaming) ? lastBlockRef.current.source.slice(0, KEY_PREFIX_LEN) : block.slice(0, KEY_PREFIX_LEN);
|
|
1218
|
+
var stableKey = "b".concat(i, "-").concat(contentForKey);
|
|
1190
1219
|
if (!isLast) {
|
|
1191
1220
|
var cached = cache.get(block);
|
|
1192
1221
|
if (cached && cached.source === block) {
|