@ant-design/agentic-ui 2.30.4 → 2.30.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/Bubble/AIBubble.js +15 -5
- package/dist/Bubble/List/PureBubbleList.js +16 -4
- package/dist/Bubble/List/index.js +18 -4
- package/dist/Bubble/MessagesContent/MarkdownPreview.js +6 -4
- package/dist/MarkdownEditor/types.d.ts +11 -1
- package/dist/MarkdownRenderer/AnimationText.d.ts +6 -2
- package/dist/MarkdownRenderer/AnimationText.js +26 -128
- package/dist/MarkdownRenderer/MarkdownRenderer.js +18 -8
- package/dist/MarkdownRenderer/StreamingAnimationContext.d.ts +6 -0
- package/dist/MarkdownRenderer/StreamingAnimationContext.js +2 -0
- package/dist/MarkdownRenderer/types.d.ts +9 -1
- package/dist/MarkdownRenderer/useMarkdownToReact.d.ts +5 -0
- package/dist/MarkdownRenderer/useMarkdownToReact.js +83 -32
- 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,
|
|
@@ -125,20 +125,22 @@ import { MessagesContext } from "./BubbleContext";
|
|
|
125
125
|
var _content_includes, _htmlRef_current, _htmlRef_current1, _props_markdownRenderConfig, _props_originData, _props_originData1, _props_originData2, _props_markdownRenderConfig1, _props_markdownRenderConfig2, _props_markdownRenderConfig3;
|
|
126
126
|
// MarkdownRenderer 渲染路径——轻量,不创建 Slate 实例
|
|
127
127
|
if (renderMode === 'markdown') {
|
|
128
|
-
var _props_originData3, _props_markdownRenderConfig4, _props_markdownRenderConfig5, _props_markdownRenderConfig6, _props_markdownRenderConfig7;
|
|
128
|
+
var _props_originData3, _props_markdownRenderConfig4, _props_markdownRenderConfig5, _props_markdownRenderConfig6, _props_markdownRenderConfig7, _props_markdownRenderConfig8, _props_markdownRenderConfig9;
|
|
129
129
|
return /*#__PURE__*/ React.createElement(MarkdownRenderer, {
|
|
130
130
|
content: content,
|
|
131
131
|
streaming: typing,
|
|
132
132
|
isFinished: (_props_originData3 = props.originData) === null || _props_originData3 === void 0 ? void 0 : _props_originData3.isFinished,
|
|
133
133
|
plugins: (_props_markdownRenderConfig4 = props.markdownRenderConfig) === null || _props_markdownRenderConfig4 === void 0 ? void 0 : _props_markdownRenderConfig4.plugins,
|
|
134
|
+
queueOptions: (_props_markdownRenderConfig5 = props.markdownRenderConfig) === null || _props_markdownRenderConfig5 === void 0 ? void 0 : _props_markdownRenderConfig5.queueOptions,
|
|
135
|
+
streamingParagraphAnimation: (_props_markdownRenderConfig6 = props.markdownRenderConfig) === null || _props_markdownRenderConfig6 === void 0 ? void 0 : _props_markdownRenderConfig6.streamingParagraphAnimation,
|
|
134
136
|
fncProps: fncProps,
|
|
135
137
|
style: _object_spread({
|
|
136
138
|
maxWidth: standalone ? '100%' : undefined,
|
|
137
139
|
padding: isPaddingHidden ? 0 : undefined,
|
|
138
140
|
margin: isPaddingHidden ? 0 : undefined
|
|
139
|
-
}, ((
|
|
140
|
-
codeProps: (
|
|
141
|
-
apaasify: (
|
|
141
|
+
}, ((_props_markdownRenderConfig7 = props.markdownRenderConfig) === null || _props_markdownRenderConfig7 === void 0 ? void 0 : _props_markdownRenderConfig7.style) || {}),
|
|
142
|
+
codeProps: (_props_markdownRenderConfig8 = props.markdownRenderConfig) === null || _props_markdownRenderConfig8 === void 0 ? void 0 : _props_markdownRenderConfig8.codeProps,
|
|
143
|
+
apaasify: (_props_markdownRenderConfig9 = props.markdownRenderConfig) === null || _props_markdownRenderConfig9 === void 0 ? void 0 : _props_markdownRenderConfig9.apaasify
|
|
142
144
|
});
|
|
143
145
|
}
|
|
144
146
|
// Slate 渲染路径——保持向后兼容
|
|
@@ -4,7 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import { BaseEditor, Editor, Selection } from 'slate';
|
|
5
5
|
import { HistoryEditor } from 'slate-history';
|
|
6
6
|
import { ReactEditor, RenderElementProps } from 'slate-react';
|
|
7
|
-
import type { RenderMode } from '../MarkdownRenderer/types';
|
|
7
|
+
import type { CharacterQueueOptions, RenderMode } from '../MarkdownRenderer/types';
|
|
8
8
|
import { TagPopupProps } from './editor/elements/TagPopup';
|
|
9
9
|
import { EditorStore } from './editor/store';
|
|
10
10
|
import { InsertAutocompleteProps } from './editor/tools/InsertAutocomplete';
|
|
@@ -473,6 +473,16 @@ export type MarkdownEditorProps = {
|
|
|
473
473
|
/** 自定义链接渲染函数 */
|
|
474
474
|
onClick?: (url?: string) => boolean | void;
|
|
475
475
|
};
|
|
476
|
+
/**
|
|
477
|
+
* MarkdownRenderer 字符队列(仅 `renderMode: 'markdown'` 只读预览时生效)
|
|
478
|
+
* @description 默认关闭逐字 RAF;需要打字机时再设 `{ animate: true, animateTailChars: 50 }` 等
|
|
479
|
+
*/
|
|
480
|
+
queueOptions?: CharacterQueueOptions;
|
|
481
|
+
/**
|
|
482
|
+
* 流式 Markdown 末段淡入(仅 `renderMode: 'markdown'` 时传给 MarkdownRenderer)
|
|
483
|
+
* @description 默认 false;设为 true 时末段使用 AnimationText 入场,可能在高频更新时产生闪动
|
|
484
|
+
*/
|
|
485
|
+
streamingParagraphAnimation?: boolean;
|
|
476
486
|
/**
|
|
477
487
|
* 依赖数组
|
|
478
488
|
* @description 用于控制 MElement 组件是否刷新的依赖数组。当 deps 数组内容发生变化时,MElement 会重新渲染
|
|
@@ -4,6 +4,11 @@ export interface AnimationConfig {
|
|
|
4
4
|
fadeDuration?: number;
|
|
5
5
|
/** 缓动函数,默认 ease-out */
|
|
6
6
|
easing?: string;
|
|
7
|
+
/**
|
|
8
|
+
* 已废弃:单段入场动画模式下不再按 chunk 瘦身 DOM,保留仅为类型兼容
|
|
9
|
+
* @deprecated
|
|
10
|
+
*/
|
|
11
|
+
collapseThreshold?: number;
|
|
7
12
|
}
|
|
8
13
|
export interface AnimationTextProps {
|
|
9
14
|
children: React.ReactNode;
|
|
@@ -12,8 +17,7 @@ export interface AnimationTextProps {
|
|
|
12
17
|
/**
|
|
13
18
|
* 流式文字淡入动画组件。
|
|
14
19
|
*
|
|
15
|
-
*
|
|
16
|
-
* 动画结束后通过 animationend 标记 done,仍用 span 包裹以保持布局稳定。
|
|
20
|
+
* 同一段流式前缀追加(或尾部截断修正)只触发一次入场动画;非前缀替换时重播。
|
|
17
21
|
*/
|
|
18
22
|
declare const AnimationText: React.NamedExoticComponent<AnimationTextProps>;
|
|
19
23
|
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);
|
|
@@ -121,71 +57,43 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
121
57
|
}
|
|
122
58
|
return '';
|
|
123
59
|
};
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
* 富文本结构(链接、脚注、图片等)在纯文本差分下会丢失结构信息,直接透传更安全。
|
|
127
|
-
*/ var hasElementNode = function hasElementNode1(children) {
|
|
128
|
-
if (children === null || children === undefined || typeof children === 'boolean') return false;
|
|
129
|
-
if (typeof children === 'string' || typeof children === 'number') return false;
|
|
130
|
-
if (Array.isArray(children)) return children.some(hasElementNode);
|
|
131
|
-
return /*#__PURE__*/ React.isValidElement(children);
|
|
60
|
+
/** 流式同一段内:前后文案仅「前缀关系」变化(增长或尾部截断修正) */ var isStreamingCompatible = function isStreamingCompatible(prev, next) {
|
|
61
|
+
return prev.startsWith(next) || next.startsWith(prev);
|
|
132
62
|
};
|
|
133
63
|
/**
|
|
134
64
|
* 流式文字淡入动画组件。
|
|
135
65
|
*
|
|
136
|
-
*
|
|
137
|
-
* 动画结束后通过 animationend 标记 done,仍用 span 包裹以保持布局稳定。
|
|
66
|
+
* 同一段流式前缀追加(或尾部截断修正)只触发一次入场动画;非前缀替换时重播。
|
|
138
67
|
*/ var AnimationText = /*#__PURE__*/ React.memo(function(param) {
|
|
139
68
|
var children = param.children, animationConfig = param.animationConfig;
|
|
140
|
-
var _ref = animationConfig || {}, _ref_fadeDuration = _ref.fadeDuration, fadeDuration = _ref_fadeDuration === void 0 ?
|
|
141
|
-
var _useState = _sliced_to_array(useState(
|
|
69
|
+
var _ref = animationConfig || {}, _ref_fadeDuration = _ref.fadeDuration, fadeDuration = _ref_fadeDuration === void 0 ? 250 : _ref_fadeDuration, _ref_easing = _ref.easing, easing = _ref_easing === void 0 ? 'ease-out' : _ref_easing;
|
|
70
|
+
var _useState = _sliced_to_array(useState(false), 2), animComplete = _useState[0], setAnimComplete = _useState[1];
|
|
71
|
+
var _useState1 = _sliced_to_array(useState(0), 2), animSession = _useState1[0], setAnimSession = _useState1[1];
|
|
142
72
|
var prevTextRef = useRef('');
|
|
143
73
|
var text = extractText(children);
|
|
144
|
-
var hasElementContent = hasElementNode(children);
|
|
145
74
|
useEffect(function() {
|
|
146
|
-
if (
|
|
75
|
+
if (text === prevTextRef.current) return;
|
|
76
|
+
var prev = prevTextRef.current;
|
|
77
|
+
if (!prev) {
|
|
78
|
+
if (!text) return;
|
|
147
79
|
prevTextRef.current = text;
|
|
80
|
+
setAnimComplete(false);
|
|
148
81
|
return;
|
|
149
82
|
}
|
|
150
|
-
if (text
|
|
151
|
-
if (!prevTextRef.current || !text.startsWith(prevTextRef.current)) {
|
|
152
|
-
setChunks([
|
|
153
|
-
{
|
|
154
|
-
key: '0',
|
|
155
|
-
text: text,
|
|
156
|
-
content: children,
|
|
157
|
-
done: false
|
|
158
|
-
}
|
|
159
|
-
]);
|
|
83
|
+
if (isStreamingCompatible(prev, text)) {
|
|
160
84
|
prevTextRef.current = text;
|
|
161
85
|
return;
|
|
162
86
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
setChunks(function(prev) {
|
|
167
|
-
return _to_consumable_array(prev).concat([
|
|
168
|
-
{
|
|
169
|
-
key: newKey,
|
|
170
|
-
text: newText,
|
|
171
|
-
done: false
|
|
172
|
-
}
|
|
173
|
-
]);
|
|
87
|
+
setAnimComplete(false);
|
|
88
|
+
setAnimSession(function(s) {
|
|
89
|
+
return s + 1;
|
|
174
90
|
});
|
|
175
91
|
prevTextRef.current = text;
|
|
176
92
|
}, [
|
|
177
|
-
text
|
|
178
|
-
children,
|
|
179
|
-
hasElementContent
|
|
93
|
+
text
|
|
180
94
|
]);
|
|
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
|
-
});
|
|
95
|
+
var handleAnimationEnd = function handleAnimationEnd() {
|
|
96
|
+
return setAnimComplete(true);
|
|
189
97
|
};
|
|
190
98
|
var animationStyle = useMemo(function() {
|
|
191
99
|
return {
|
|
@@ -198,29 +106,19 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
198
106
|
fadeDuration,
|
|
199
107
|
easing
|
|
200
108
|
]);
|
|
201
|
-
|
|
109
|
+
var doneChunkStyle = useMemo(function() {
|
|
202
110
|
return {
|
|
203
111
|
display: 'inline-block',
|
|
204
112
|
color: 'inherit'
|
|
205
113
|
};
|
|
206
114
|
}, []);
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
key: chunk.key,
|
|
215
|
-
style: doneChunkStyle
|
|
216
|
-
}, rendered) : /*#__PURE__*/ React.createElement("span", {
|
|
217
|
-
key: chunk.key,
|
|
218
|
-
style: animationStyle,
|
|
219
|
-
onAnimationEnd: function onAnimationEnd() {
|
|
220
|
-
return handleAnimationEnd(chunk.key);
|
|
221
|
-
}
|
|
222
|
-
}, rendered);
|
|
223
|
-
}));
|
|
115
|
+
return animComplete ? /*#__PURE__*/ React.createElement("span", {
|
|
116
|
+
style: doneChunkStyle
|
|
117
|
+
}, children) : /*#__PURE__*/ React.createElement("span", {
|
|
118
|
+
key: animSession,
|
|
119
|
+
style: animationStyle,
|
|
120
|
+
onAnimationEnd: handleAnimationEnd
|
|
121
|
+
}, children);
|
|
224
122
|
});
|
|
225
123
|
AnimationText.displayName = 'AnimationText';
|
|
226
124
|
export default AnimationText;
|
|
@@ -236,7 +236,7 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
236
236
|
* - Markdown → hast → React 元素树(hast-util-to-jsx-runtime)
|
|
237
237
|
* - 特殊块(code / mermaid / chart / katex)通过组件映射拦截渲染
|
|
238
238
|
*/ var InternalMarkdownRenderer = /*#__PURE__*/ forwardRef(function(props, ref) {
|
|
239
|
-
var content = props.content, _props_streaming = props.streaming, streaming = _props_streaming === void 0 ? false : _props_streaming, isFinished = props.isFinished, queueOptions = props.queueOptions, plugins = props.plugins, remarkPlugins = props.remarkPlugins, htmlConfig = props.htmlConfig, className = props.className, style = props.style, customPrefixCls = props.prefixCls, linkConfig = props.linkConfig, apaasify = props.apaasify;
|
|
239
|
+
var content = props.content, _props_streaming = props.streaming, streaming = _props_streaming === void 0 ? false : _props_streaming, isFinished = props.isFinished, queueOptions = props.queueOptions, plugins = props.plugins, remarkPlugins = props.remarkPlugins, htmlConfig = props.htmlConfig, className = props.className, style = props.style, customPrefixCls = props.prefixCls, linkConfig = props.linkConfig, streamingParagraphAnimation = props.streamingParagraphAnimation, apaasify = props.apaasify;
|
|
240
240
|
var getPrefixCls = useContext(ConfigProvider.ConfigContext).getPrefixCls;
|
|
241
241
|
// 复用 MarkdownEditor 的 CSS 前缀和样式,保持渲染一致性
|
|
242
242
|
var prefixCls = getPrefixCls('agentic-md-editor', customPrefixCls);
|
|
@@ -249,6 +249,7 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
249
249
|
var containerRef = useRef(null);
|
|
250
250
|
var _useState = _sliced_to_array(useState(content || ''), 2), displayedContent = _useState[0], setDisplayedContent = _useState[1];
|
|
251
251
|
var queueRef = useRef(null);
|
|
252
|
+
/** 与 CharacterQueue 构造参数同步,避免 queueOptions 变更后仍用旧队列行为 */ var queueOptsSigRef = useRef('');
|
|
252
253
|
useImperativeHandle(ref, function() {
|
|
253
254
|
return {
|
|
254
255
|
nativeElement: containerRef.current,
|
|
@@ -263,10 +264,12 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
263
264
|
}, [
|
|
264
265
|
plugins
|
|
265
266
|
]);
|
|
266
|
-
//
|
|
267
|
+
// 字符队列:默认关闭逐字动画,避免 RAF 每帧全量重解析 Markdown 导致整页闪动。
|
|
268
|
+
// 需要打字机效果时显式传入 queueOptions={{ animate: true, animateTailChars?: number }}。
|
|
267
269
|
var resolvedQueueOptions = useMemo(function() {
|
|
268
270
|
return streaming ? _object_spread({
|
|
269
|
-
|
|
271
|
+
animate: false,
|
|
272
|
+
animateTailChars: undefined
|
|
270
273
|
}, queueOptions) : queueOptions;
|
|
271
274
|
}, [
|
|
272
275
|
streaming,
|
|
@@ -274,17 +277,23 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
274
277
|
]);
|
|
275
278
|
useEffect(function() {
|
|
276
279
|
if (!streaming) {
|
|
277
|
-
|
|
280
|
+
var _queueRef_current;
|
|
278
281
|
setDisplayedContent(content || '');
|
|
282
|
+
(_queueRef_current = queueRef.current) === null || _queueRef_current === void 0 ? void 0 : _queueRef_current.dispose();
|
|
283
|
+
queueRef.current = null;
|
|
284
|
+
queueOptsSigRef.current = '';
|
|
279
285
|
return;
|
|
280
286
|
}
|
|
281
|
-
|
|
287
|
+
var sig = JSON.stringify(resolvedQueueOptions !== null && resolvedQueueOptions !== void 0 ? resolvedQueueOptions : {});
|
|
288
|
+
if (!queueRef.current || sig !== queueOptsSigRef.current) {
|
|
289
|
+
var _queueRef_current1;
|
|
290
|
+
(_queueRef_current1 = queueRef.current) === null || _queueRef_current1 === void 0 ? void 0 : _queueRef_current1.dispose();
|
|
282
291
|
queueRef.current = new CharacterQueue(function(displayed) {
|
|
283
292
|
return setDisplayedContent(displayed);
|
|
284
293
|
}, resolvedQueueOptions);
|
|
294
|
+
queueOptsSigRef.current = sig;
|
|
285
295
|
}
|
|
286
296
|
queueRef.current.push(content || '');
|
|
287
|
-
return undefined;
|
|
288
297
|
}, [
|
|
289
298
|
content,
|
|
290
299
|
streaming,
|
|
@@ -347,7 +356,8 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
347
356
|
components: components,
|
|
348
357
|
prefixCls: prefixCls,
|
|
349
358
|
linkConfig: linkConfig,
|
|
350
|
-
streaming: streaming
|
|
359
|
+
streaming: streaming,
|
|
360
|
+
streamingParagraphAnimation: streamingParagraphAnimation
|
|
351
361
|
});
|
|
352
362
|
return wrapVarSSR(wrapSSR(wrapContentSSR(/*#__PURE__*/ React.createElement("div", {
|
|
353
363
|
ref: containerRef,
|
|
@@ -359,7 +369,7 @@ var SCHEMA_LANGUAGES = new Set([
|
|
|
359
369
|
display: 'block'
|
|
360
370
|
}
|
|
361
371
|
}, /*#__PURE__*/ React.createElement("div", {
|
|
362
|
-
className: clsx(contentCls, hashId),
|
|
372
|
+
className: clsx(contentCls, "".concat(contentCls, "-markdown-readonly"), hashId),
|
|
363
373
|
style: {
|
|
364
374
|
whiteSpace: 'normal',
|
|
365
375
|
wordWrap: 'normal'
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/** 仅末块为 true:流式 Markdown 只对「当前生长块」播放入场,避免全页段落/表格单元格一起闪动 */
|
|
3
|
+
export interface StreamingAnimationContextValue {
|
|
4
|
+
animateBlock: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare const StreamingAnimationContext: React.Context<StreamingAnimationContextValue | null>;
|
|
@@ -4,7 +4,10 @@ import type { MarkdownEditorPlugin } from '../MarkdownEditor/plugin';
|
|
|
4
4
|
export interface CharacterQueueOptions {
|
|
5
5
|
/** 每帧输出的最大字符数,默认 3 */
|
|
6
6
|
charsPerFrame?: number;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* 是否启用 CharacterQueue 逐字输出(RAF 驱动)。
|
|
9
|
+
* MarkdownRenderer 流式默认合并为 `false`,避免每帧全量重解析导致整页闪动;需打字机时再设为 `true`。
|
|
10
|
+
*/
|
|
8
11
|
animate?: boolean;
|
|
9
12
|
/**
|
|
10
13
|
* 仅对末尾 N 个字符做动画,前面内容立即展示。
|
|
@@ -65,6 +68,11 @@ export interface MarkdownRendererProps {
|
|
|
65
68
|
/** 自定义链接点击处理,返回 false 可阻止默认跳转 */
|
|
66
69
|
onClick?: (url?: string) => boolean | void;
|
|
67
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* 流式时是否为生长中的末段启用淡入(AnimationText)。
|
|
73
|
+
* 默认 false,避免重解析时整页闪动;需要段落入场效果时再设为 true。
|
|
74
|
+
*/
|
|
75
|
+
streamingParagraphAnimation?: boolean;
|
|
68
76
|
/** Apaasify / Schema 自定义渲染 */
|
|
69
77
|
apaasify?: {
|
|
70
78
|
enable?: boolean;
|
|
@@ -14,6 +14,11 @@ interface UseMarkdownToReactOptions {
|
|
|
14
14
|
};
|
|
15
15
|
/** 是否处于流式状态,用于最后一个块的打字动画 */
|
|
16
16
|
streaming?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 流式时是否对「生长中的末段」启用段落淡入(AnimationText)。
|
|
19
|
+
* 默认 false:重解析时频繁触发动画易导致整页闪动;需要时再显式传入 true。
|
|
20
|
+
*/
|
|
21
|
+
streamingParagraphAnimation?: boolean;
|
|
17
22
|
}
|
|
18
23
|
export declare const useMarkdownToReact: (content: string, options?: UseMarkdownToReactOptions) => React.ReactNode;
|
|
19
24
|
/**
|
|
@@ -122,7 +122,7 @@ function _unsupported_iterable_to_array(o, minLen) {
|
|
|
122
122
|
}
|
|
123
123
|
import { Checkbox, Image } from "antd";
|
|
124
124
|
import { toJsxRuntime } from "hast-util-to-jsx-runtime";
|
|
125
|
-
import React, { useMemo, useRef } from "react";
|
|
125
|
+
import React, { useContext, useMemo, useRef } from "react";
|
|
126
126
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
127
127
|
import rehypeKatex from "rehype-katex";
|
|
128
128
|
import rehypeRaw from "rehype-raw";
|
|
@@ -141,6 +141,7 @@ import { REMARK_REHYPE_DIRECTIVE_HANDLERS } from "../MarkdownEditor/editor/utils
|
|
|
141
141
|
import { parseChineseCurrencyToNumber } from "../Plugins/chart/utils";
|
|
142
142
|
import { ToolUseBarThink } from "../ToolUseBarThink";
|
|
143
143
|
import AnimationText from "./AnimationText";
|
|
144
|
+
import { StreamingAnimationContext } from "./StreamingAnimationContext";
|
|
144
145
|
var INLINE_MATH_WITH_SINGLE_DOLLAR = {
|
|
145
146
|
singleDollarTextMath: true
|
|
146
147
|
};
|
|
@@ -432,14 +433,26 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
432
433
|
*
|
|
433
434
|
* MarkdownEditor 的 Slate 元素使用 data-be 属性和 prefixCls 类名,
|
|
434
435
|
* 这里为原生 HTML 标签添加相同的属性,使共用的 CSS 能正确命中。
|
|
435
|
-
*/ var buildEditorAlignedComponents = function buildEditorAlignedComponents(prefixCls, userComponents, streaming, linkConfig) {
|
|
436
|
+
*/ var buildEditorAlignedComponents = function buildEditorAlignedComponents(prefixCls, userComponents, streaming, linkConfig, streamingParagraphAnimation) {
|
|
436
437
|
var listCls = "".concat(prefixCls, "-list");
|
|
437
438
|
var tableCls = "".concat(prefixCls, "-content-table");
|
|
438
439
|
var contentCls = prefixCls; // e.g. ant-agentic-md-editor-content
|
|
440
|
+
/** 仅当 streaming、末块动画上下文允许且显式开启段落动画时包 AnimationText */ var StreamAnimWrap = function StreamAnimWrap(param) {
|
|
441
|
+
var children = param.children;
|
|
442
|
+
var _ref;
|
|
443
|
+
var ctx = useContext(StreamingAnimationContext);
|
|
444
|
+
var animateBlock = (_ref = ctx === null || ctx === void 0 ? void 0 : ctx.animateBlock) !== null && _ref !== void 0 ? _ref : true;
|
|
445
|
+
var allow = !!streaming && animateBlock && !!streamingParagraphAnimation;
|
|
446
|
+
if (!allow) return children;
|
|
447
|
+
return jsx(AnimationText, {
|
|
448
|
+
children: children
|
|
449
|
+
});
|
|
450
|
+
};
|
|
451
|
+
StreamAnimWrap.displayName = 'StreamAnimWrap';
|
|
439
452
|
var wrapAnimation = function wrapAnimation(children) {
|
|
440
|
-
return
|
|
453
|
+
return jsx(StreamAnimWrap, {
|
|
441
454
|
children: children
|
|
442
|
-
})
|
|
455
|
+
});
|
|
443
456
|
};
|
|
444
457
|
return _object_spread({
|
|
445
458
|
// ================================================================
|
|
@@ -464,7 +477,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
464
477
|
return jsx('h1', _object_spread_props(_object_spread({}, rest), {
|
|
465
478
|
'data-be': 'head',
|
|
466
479
|
'data-testid': 'markdown-heading-1',
|
|
467
|
-
children:
|
|
480
|
+
children: children
|
|
468
481
|
}));
|
|
469
482
|
},
|
|
470
483
|
h2: function h2(props) {
|
|
@@ -475,7 +488,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
475
488
|
return jsx('h2', _object_spread_props(_object_spread({}, rest), {
|
|
476
489
|
'data-be': 'head',
|
|
477
490
|
'data-testid': 'markdown-heading-2',
|
|
478
|
-
children:
|
|
491
|
+
children: children
|
|
479
492
|
}));
|
|
480
493
|
},
|
|
481
494
|
h3: function h3(props) {
|
|
@@ -486,7 +499,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
486
499
|
return jsx('h3', _object_spread_props(_object_spread({}, rest), {
|
|
487
500
|
'data-be': 'head',
|
|
488
501
|
'data-testid': 'markdown-heading-3',
|
|
489
|
-
children:
|
|
502
|
+
children: children
|
|
490
503
|
}));
|
|
491
504
|
},
|
|
492
505
|
h4: function h4(props) {
|
|
@@ -497,7 +510,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
497
510
|
return jsx('h4', _object_spread_props(_object_spread({}, rest), {
|
|
498
511
|
'data-be': 'head',
|
|
499
512
|
'data-testid': 'markdown-heading-4',
|
|
500
|
-
children:
|
|
513
|
+
children: children
|
|
501
514
|
}));
|
|
502
515
|
},
|
|
503
516
|
h5: function h5(props) {
|
|
@@ -508,7 +521,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
508
521
|
return jsx('h5', _object_spread_props(_object_spread({}, rest), {
|
|
509
522
|
'data-be': 'head',
|
|
510
523
|
'data-testid': 'markdown-heading-5',
|
|
511
|
-
children:
|
|
524
|
+
children: children
|
|
512
525
|
}));
|
|
513
526
|
},
|
|
514
527
|
h6: function h6(props) {
|
|
@@ -519,7 +532,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
519
532
|
return jsx('h6', _object_spread_props(_object_spread({}, rest), {
|
|
520
533
|
'data-be': 'head',
|
|
521
534
|
'data-testid': 'markdown-heading-6',
|
|
522
|
-
children:
|
|
535
|
+
children: children
|
|
523
536
|
}));
|
|
524
537
|
},
|
|
525
538
|
blockquote: function blockquote(props) {
|
|
@@ -607,7 +620,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
607
620
|
className: "".concat(listCls, "-item"),
|
|
608
621
|
'data-be': 'list-item',
|
|
609
622
|
'data-testid': 'markdown-list-item',
|
|
610
|
-
children:
|
|
623
|
+
children: children
|
|
611
624
|
}));
|
|
612
625
|
},
|
|
613
626
|
table: function table(props) {
|
|
@@ -686,7 +699,7 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
686
699
|
whiteSpace: 'normal',
|
|
687
700
|
maxWidth: '20%'
|
|
688
701
|
},
|
|
689
|
-
children:
|
|
702
|
+
children: children
|
|
690
703
|
}));
|
|
691
704
|
},
|
|
692
705
|
// input[type=checkbox]:task list 的 checkbox(兜底,主逻辑在 li 中)
|
|
@@ -1061,13 +1074,31 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
1061
1074
|
}
|
|
1062
1075
|
}, userComponents);
|
|
1063
1076
|
};
|
|
1077
|
+
/**
|
|
1078
|
+
* 在 hast 上标记「最后一个 p」,用于流式时仅该段落播放入场(单块长文时避免全页 p 一起闪)
|
|
1079
|
+
*/ var markLastParagraphStreamingTail = function markLastParagraphStreamingTail(hast) {
|
|
1080
|
+
var paragraphs = [];
|
|
1081
|
+
visit(hast, 'element', function(node) {
|
|
1082
|
+
if (node.tagName === 'p') {
|
|
1083
|
+
paragraphs.push(node);
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
var last = paragraphs[paragraphs.length - 1];
|
|
1087
|
+
if (last) {
|
|
1088
|
+
last.properties = last.properties || {};
|
|
1089
|
+
last.properties.dataStreamingTail = true;
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1064
1092
|
/**
|
|
1065
1093
|
* 将单个 markdown 片段转为 React 元素(内部函数)
|
|
1066
|
-
*/ var renderMarkdownBlock = function renderMarkdownBlock(blockContent, processor, components) {
|
|
1094
|
+
*/ var renderMarkdownBlock = function renderMarkdownBlock(blockContent, processor, components, blockOpts) {
|
|
1067
1095
|
if (!blockContent.trim()) return null;
|
|
1068
1096
|
try {
|
|
1069
1097
|
var mdast = processor.parse(blockContent);
|
|
1070
1098
|
var hast = processor.runSync(mdast);
|
|
1099
|
+
if (blockOpts === null || blockOpts === void 0 ? void 0 : blockOpts.markStreamingTailParagraph) {
|
|
1100
|
+
markLastParagraphStreamingTail(hast);
|
|
1101
|
+
}
|
|
1071
1102
|
return toJsxRuntime(hast, {
|
|
1072
1103
|
Fragment: Fragment,
|
|
1073
1104
|
jsx: jsx,
|
|
@@ -1098,7 +1129,9 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
|
|
|
1098
1129
|
if (!inFence && line === '' && current.length > 0) {
|
|
1099
1130
|
var prev = current[current.length - 1];
|
|
1100
1131
|
if (prev === '') {
|
|
1101
|
-
|
|
1132
|
+
// 触发分割的是「第二个连续空行」,不应并入上一块末尾,否则与单块解析结果字符串不一致、缓存失效
|
|
1133
|
+
var withoutTrailingBlank = current.slice(0, -1);
|
|
1134
|
+
blocks.push(withoutTrailingBlank.join('\n'));
|
|
1102
1135
|
current = [];
|
|
1103
1136
|
continue;
|
|
1104
1137
|
}
|
|
@@ -1152,6 +1185,7 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1152
1185
|
var processorRef = useRef(null);
|
|
1153
1186
|
var blockCacheRef = useRef(new Map());
|
|
1154
1187
|
var lastBlockRef = useRef(null);
|
|
1188
|
+
var prevContentRef = useRef('');
|
|
1155
1189
|
var processor = useMemo(function() {
|
|
1156
1190
|
var p = createHastProcessor(options === null || options === void 0 ? void 0 : options.remarkPlugins, options === null || options === void 0 ? void 0 : options.htmlConfig);
|
|
1157
1191
|
processorRef.current = p;
|
|
@@ -1163,15 +1197,25 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1163
1197
|
var prefixCls = (options === null || options === void 0 ? void 0 : options.prefixCls) || 'ant-agentic-md-editor';
|
|
1164
1198
|
var components = useMemo(function() {
|
|
1165
1199
|
var userComponents = (options === null || options === void 0 ? void 0 : options.components) || {};
|
|
1166
|
-
return buildEditorAlignedComponents(prefixCls, userComponents, options === null || options === void 0 ? void 0 : options.streaming, options === null || options === void 0 ? void 0 : options.linkConfig);
|
|
1200
|
+
return buildEditorAlignedComponents(prefixCls, userComponents, options === null || options === void 0 ? void 0 : options.streaming, options === null || options === void 0 ? void 0 : options.linkConfig, options === null || options === void 0 ? void 0 : options.streamingParagraphAnimation);
|
|
1167
1201
|
}, [
|
|
1168
1202
|
prefixCls,
|
|
1169
1203
|
options === null || options === void 0 ? void 0 : options.components,
|
|
1170
1204
|
options === null || options === void 0 ? void 0 : options.streaming,
|
|
1171
|
-
options === null || options === void 0 ? void 0 : options.linkConfig
|
|
1205
|
+
options === null || options === void 0 ? void 0 : options.linkConfig,
|
|
1206
|
+
options === null || options === void 0 ? void 0 : options.streamingParagraphAnimation
|
|
1172
1207
|
]);
|
|
1173
1208
|
return useMemo(function() {
|
|
1174
|
-
if (!content)
|
|
1209
|
+
if (!content) {
|
|
1210
|
+
prevContentRef.current = '';
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1213
|
+
var prevContent = prevContentRef.current;
|
|
1214
|
+
if (prevContent && content !== prevContent && !content.startsWith(prevContent)) {
|
|
1215
|
+
blockCacheRef.current = new Map();
|
|
1216
|
+
lastBlockRef.current = null;
|
|
1217
|
+
}
|
|
1218
|
+
prevContentRef.current = content;
|
|
1175
1219
|
try {
|
|
1176
1220
|
var preprocessed = content.replace(new RegExp(JINJA_DOLLAR_PLACEHOLDER, 'g'), '$');
|
|
1177
1221
|
var blocks = splitMarkdownBlocks(preprocessed);
|
|
@@ -1179,21 +1223,30 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1179
1223
|
var cache = blockCacheRef.current;
|
|
1180
1224
|
var newCache = new Map();
|
|
1181
1225
|
var elements = [];
|
|
1226
|
+
var wrapBlockScope = function wrapBlockScope(node, key, animateBlock) {
|
|
1227
|
+
return jsx(StreamingAnimationContext.Provider, {
|
|
1228
|
+
key: key,
|
|
1229
|
+
value: {
|
|
1230
|
+
animateBlock: animateBlock
|
|
1231
|
+
},
|
|
1232
|
+
children: node
|
|
1233
|
+
});
|
|
1234
|
+
};
|
|
1235
|
+
var KEY_PREFIX_LEN = 64;
|
|
1182
1236
|
for(var i = 0; i < blocks.length; i++){
|
|
1183
1237
|
var block = blocks[i];
|
|
1184
1238
|
var isLast = i === blocks.length - 1;
|
|
1185
1239
|
// 用 index + 内容前 64 字符作 key,保持稳定性:
|
|
1186
|
-
//
|
|
1187
|
-
//
|
|
1188
|
-
//
|
|
1189
|
-
var
|
|
1240
|
+
// - 末块在流式中节流时,用 lastBlockRef.source 的切片作 key,避免每次追加字符导致 key 变化
|
|
1241
|
+
// - 末块变为非末块时,必须与先前 key 一致,否则 ChartBlockRenderer 等会 unmount/remount 闪烁
|
|
1242
|
+
// - 因此统一用「实际展示内容」的 slice 作 key,节流时用 lastBlockRef.source
|
|
1243
|
+
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);
|
|
1244
|
+
var stableKey = "b".concat(i, "-").concat(contentForKey);
|
|
1190
1245
|
if (!isLast) {
|
|
1191
1246
|
var cached = cache.get(block);
|
|
1192
1247
|
if (cached && cached.source === block) {
|
|
1193
1248
|
newCache.set(block, cached);
|
|
1194
|
-
elements.push(
|
|
1195
|
-
children: cached.element
|
|
1196
|
-
}, stableKey));
|
|
1249
|
+
elements.push(wrapBlockScope(cached.element, stableKey, false));
|
|
1197
1250
|
continue;
|
|
1198
1251
|
}
|
|
1199
1252
|
}
|
|
@@ -1204,22 +1257,20 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1204
1257
|
source: lastBlockRef.current.source,
|
|
1205
1258
|
element: lastBlockRef.current.element
|
|
1206
1259
|
});
|
|
1207
|
-
elements.push(
|
|
1208
|
-
children: lastBlockRef.current.element
|
|
1209
|
-
}, stableKey));
|
|
1260
|
+
elements.push(wrapBlockScope(lastBlockRef.current.element, stableKey, !!(options === null || options === void 0 ? void 0 : options.streaming)));
|
|
1210
1261
|
continue;
|
|
1211
1262
|
}
|
|
1212
1263
|
}
|
|
1213
|
-
var element = renderMarkdownBlock(block, processor, components
|
|
1264
|
+
var element = renderMarkdownBlock(block, processor, components, {
|
|
1265
|
+
markStreamingTailParagraph: isLast && !!(options === null || options === void 0 ? void 0 : options.streaming)
|
|
1266
|
+
});
|
|
1214
1267
|
var entry = {
|
|
1215
1268
|
source: block,
|
|
1216
1269
|
element: element
|
|
1217
1270
|
};
|
|
1218
1271
|
newCache.set(block, entry);
|
|
1219
1272
|
if (isLast) lastBlockRef.current = entry;
|
|
1220
|
-
elements.push(
|
|
1221
|
-
children: element
|
|
1222
|
-
}, stableKey));
|
|
1273
|
+
elements.push(wrapBlockScope(element, stableKey, isLast && !!(options === null || options === void 0 ? void 0 : options.streaming)));
|
|
1223
1274
|
}
|
|
1224
1275
|
blockCacheRef.current = newCache;
|
|
1225
1276
|
return jsxs(Fragment, {
|
|
@@ -1246,7 +1297,7 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
|
|
|
1246
1297
|
var mdast = processor.parse(preprocessed);
|
|
1247
1298
|
var hast = processor.runSync(mdast);
|
|
1248
1299
|
var userComps = components || {};
|
|
1249
|
-
var allComponents = buildEditorAlignedComponents('ant-agentic-md-editor', userComps, false);
|
|
1300
|
+
var allComponents = buildEditorAlignedComponents('ant-agentic-md-editor', userComps, false, undefined, false);
|
|
1250
1301
|
return toJsxRuntime(hast, {
|
|
1251
1302
|
Fragment: Fragment,
|
|
1252
1303
|
jsx: jsx,
|