@ant-design/agentic-ui 2.30.5 → 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.
@@ -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
- }, ((_props_markdownRenderConfig5 = props.markdownRenderConfig) === null || _props_markdownRenderConfig5 === void 0 ? void 0 : _props_markdownRenderConfig5.style) || {}),
140
- codeProps: (_props_markdownRenderConfig6 = props.markdownRenderConfig) === null || _props_markdownRenderConfig6 === void 0 ? void 0 : _props_markdownRenderConfig6.codeProps,
141
- apaasify: (_props_markdownRenderConfig7 = props.markdownRenderConfig) === null || _props_markdownRenderConfig7 === void 0 ? void 0 : _props_markdownRenderConfig7.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,9 +17,7 @@ export interface AnimationTextProps {
12
17
  /**
13
18
  * 流式文字淡入动画组件。
14
19
  *
15
- * 采用 opacity + translateY(GPU 硬件加速),清爽流派。
16
- * 同一段流式前缀追加只触发**一次**入场动画;后续增量仅更新文案。内容被替换
17
- * (非前缀增长)时重新播放入场。动画结束后仍用 span 包裹以保持布局稳定。
20
+ * 同一段流式前缀追加(或尾部截断修正)只触发一次入场动画;非前缀替换时重播。
18
21
  */
19
22
  declare const AnimationText: React.NamedExoticComponent<AnimationTextProps>;
20
23
  export default AnimationText;
@@ -57,34 +57,21 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
57
57
  }
58
58
  return '';
59
59
  };
60
- /**
61
- * 识别 children 中是否包含 React 元素节点。
62
- * 富文本结构(链接、脚注、图片等)在纯文本差分下会丢失结构信息,直接透传更安全。
63
- */ var hasElementNode = function hasElementNode1(children) {
64
- if (children === null || children === undefined || typeof children === 'boolean') return false;
65
- if (typeof children === 'string' || typeof children === 'number') return false;
66
- if (Array.isArray(children)) return children.some(hasElementNode);
67
- return /*#__PURE__*/ React.isValidElement(children);
60
+ /** 流式同一段内:前后文案仅「前缀关系」变化(增长或尾部截断修正) */ var isStreamingCompatible = function isStreamingCompatible(prev, next) {
61
+ return prev.startsWith(next) || next.startsWith(prev);
68
62
  };
69
63
  /**
70
64
  * 流式文字淡入动画组件。
71
65
  *
72
- * 采用 opacity + translateY(GPU 硬件加速),清爽流派。
73
- * 同一段流式前缀追加只触发**一次**入场动画;后续增量仅更新文案。内容被替换
74
- * (非前缀增长)时重新播放入场。动画结束后仍用 span 包裹以保持布局稳定。
66
+ * 同一段流式前缀追加(或尾部截断修正)只触发一次入场动画;非前缀替换时重播。
75
67
  */ var AnimationText = /*#__PURE__*/ React.memo(function(param) {
76
68
  var children = param.children, animationConfig = param.animationConfig;
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;
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;
78
70
  var _useState = _sliced_to_array(useState(false), 2), animComplete = _useState[0], setAnimComplete = _useState[1];
79
71
  var _useState1 = _sliced_to_array(useState(0), 2), animSession = _useState1[0], setAnimSession = _useState1[1];
80
72
  var prevTextRef = useRef('');
81
73
  var text = extractText(children);
82
- var hasElementContent = hasElementNode(children);
83
74
  useEffect(function() {
84
- if (hasElementContent) {
85
- prevTextRef.current = text;
86
- return;
87
- }
88
75
  if (text === prevTextRef.current) return;
89
76
  var prev = prevTextRef.current;
90
77
  if (!prev) {
@@ -93,7 +80,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
93
80
  setAnimComplete(false);
94
81
  return;
95
82
  }
96
- if (text.length > prev.length && text.startsWith(prev)) {
83
+ if (isStreamingCompatible(prev, text)) {
97
84
  prevTextRef.current = text;
98
85
  return;
99
86
  }
@@ -103,8 +90,7 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
103
90
  });
104
91
  prevTextRef.current = text;
105
92
  }, [
106
- text,
107
- hasElementContent
93
+ text
108
94
  ]);
109
95
  var handleAnimationEnd = function handleAnimationEnd() {
110
96
  return setAnimComplete(true);
@@ -120,15 +106,12 @@ import React, { useEffect, useMemo, useRef, useState } from "react";
120
106
  fadeDuration,
121
107
  easing
122
108
  ]);
123
- /** 动画结束后仍用 inline-block 包裹,避免从 span 变为裸内容时的宽度重排 */ var doneChunkStyle = useMemo(function() {
109
+ var doneChunkStyle = useMemo(function() {
124
110
  return {
125
111
  display: 'inline-block',
126
112
  color: 'inherit'
127
113
  };
128
114
  }, []);
129
- if (hasElementContent) {
130
- return /*#__PURE__*/ React.createElement(React.Fragment, null, children);
131
- }
132
115
  return animComplete ? /*#__PURE__*/ React.createElement("span", {
133
116
  style: doneChunkStyle
134
117
  }, children) : /*#__PURE__*/ React.createElement("span", {
@@ -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
- // 字符队列管理:流式时仅对最后 50 字做动画,避免每段逐字输出
267
+ // 字符队列:默认关闭逐字动画,避免 RAF 每帧全量重解析 Markdown 导致整页闪动。
268
+ // 需要打字机效果时显式传入 queueOptions={{ animate: true, animateTailChars?: number }}。
267
269
  var resolvedQueueOptions = useMemo(function() {
268
270
  return streaming ? _object_spread({
269
- animateTailChars: 50
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
- if (!queueRef.current) {
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>;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export var StreamingAnimationContext = /*#__PURE__*/ React.createContext(null);
@@ -4,7 +4,10 @@ import type { MarkdownEditorPlugin } from '../MarkdownEditor/plugin';
4
4
  export interface CharacterQueueOptions {
5
5
  /** 每帧输出的最大字符数,默认 3 */
6
6
  charsPerFrame?: number;
7
- /** 是否启用打字动画,默认 true(流式时) */
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 streaming ? jsx(AnimationText, {
453
+ return jsx(StreamAnimWrap, {
441
454
  children: children
442
- }) : children;
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: wrapAnimation(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: wrapAnimation(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: wrapAnimation(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: wrapAnimation(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: wrapAnimation(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: wrapAnimation(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: wrapAnimation(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: wrapAnimation(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,
@@ -1079,62 +1110,47 @@ var extractLanguageFromClassName = function extractLanguageFromClassName(classNa
1079
1110
  return null;
1080
1111
  }
1081
1112
  };
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
- };
1093
1113
  /**
1094
1114
  * 将 markdown 按块(双换行)拆分,尊重代码围栏边界。
1095
1115
  * 返回的每个块是一个独立的 markdown 片段,可单独解析。
1096
- *
1097
- * 流式且围栏未闭合时,inFence 会一直为 true,段间 `\n\n` 在按行拆分时只出现一个空行,
1098
- * 无法用「连续两个空行」检测。此时在空行处前瞻下一行:若不像围栏内代码延续则虚拟闭合围栏,
1099
- * 使末块能变为非末块且缓存键与先前一致。
1100
- */ var splitMarkdownBlocks = function splitMarkdownBlocks(content, streaming) {
1116
+ */ var splitMarkdownBlocks = function splitMarkdownBlocks(content) {
1101
1117
  var lines = content.split('\n');
1102
1118
  var blocks = [];
1103
1119
  var current = [];
1104
1120
  var inFence = false;
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'));
1121
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
1122
+ try {
1123
+ for(var _iterator = lines[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
1124
+ var line = _step.value;
1125
+ var trimmed = line.trimStart();
1126
+ if (trimmed.startsWith('```') || trimmed.startsWith('~~~')) {
1127
+ inFence = !inFence;
1128
+ }
1129
+ if (!inFence && line === '' && current.length > 0) {
1130
+ var prev = current[current.length - 1];
1131
+ if (prev === '') {
1132
+ // 触发分割的是「第二个连续空行」,不应并入上一块末尾,否则与单块解析结果字符串不一致、缓存失效
1133
+ var withoutTrailingBlank = current.slice(0, -1);
1134
+ blocks.push(withoutTrailingBlank.join('\n'));
1135
+ current = [];
1136
+ continue;
1123
1137
  }
1124
- current = [];
1125
- inFence = false;
1126
- continue;
1127
1138
  }
1139
+ current.push(line);
1128
1140
  }
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;
1141
+ } catch (err) {
1142
+ _didIteratorError = true;
1143
+ _iteratorError = err;
1144
+ } finally{
1145
+ try {
1146
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
1147
+ _iterator.return();
1148
+ }
1149
+ } finally{
1150
+ if (_didIteratorError) {
1151
+ throw _iteratorError;
1135
1152
  }
1136
1153
  }
1137
- current.push(line);
1138
1154
  }
1139
1155
  if (current.length > 0) {
1140
1156
  blocks.push(current.join('\n'));
@@ -1181,12 +1197,13 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
1181
1197
  var prefixCls = (options === null || options === void 0 ? void 0 : options.prefixCls) || 'ant-agentic-md-editor';
1182
1198
  var components = useMemo(function() {
1183
1199
  var userComponents = (options === null || options === void 0 ? void 0 : options.components) || {};
1184
- 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);
1185
1201
  }, [
1186
1202
  prefixCls,
1187
1203
  options === null || options === void 0 ? void 0 : options.components,
1188
1204
  options === null || options === void 0 ? void 0 : options.streaming,
1189
- 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
1190
1207
  ]);
1191
1208
  return useMemo(function() {
1192
1209
  if (!content) {
@@ -1201,11 +1218,20 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
1201
1218
  prevContentRef.current = content;
1202
1219
  try {
1203
1220
  var preprocessed = content.replace(new RegExp(JINJA_DOLLAR_PLACEHOLDER, 'g'), '$');
1204
- var blocks = splitMarkdownBlocks(preprocessed, options === null || options === void 0 ? void 0 : options.streaming);
1221
+ var blocks = splitMarkdownBlocks(preprocessed);
1205
1222
  if (blocks.length === 0) return null;
1206
1223
  var cache = blockCacheRef.current;
1207
1224
  var newCache = new Map();
1208
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
+ };
1209
1235
  var KEY_PREFIX_LEN = 64;
1210
1236
  for(var i = 0; i < blocks.length; i++){
1211
1237
  var block = blocks[i];
@@ -1220,9 +1246,7 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
1220
1246
  var cached = cache.get(block);
1221
1247
  if (cached && cached.source === block) {
1222
1248
  newCache.set(block, cached);
1223
- elements.push(jsx(Fragment, {
1224
- children: cached.element
1225
- }, stableKey));
1249
+ elements.push(wrapBlockScope(cached.element, stableKey, false));
1226
1250
  continue;
1227
1251
  }
1228
1252
  }
@@ -1233,22 +1257,20 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
1233
1257
  source: lastBlockRef.current.source,
1234
1258
  element: lastBlockRef.current.element
1235
1259
  });
1236
- elements.push(jsx(Fragment, {
1237
- children: lastBlockRef.current.element
1238
- }, stableKey));
1260
+ elements.push(wrapBlockScope(lastBlockRef.current.element, stableKey, !!(options === null || options === void 0 ? void 0 : options.streaming)));
1239
1261
  continue;
1240
1262
  }
1241
1263
  }
1242
- 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
+ });
1243
1267
  var entry = {
1244
1268
  source: block,
1245
1269
  element: element
1246
1270
  };
1247
1271
  newCache.set(block, entry);
1248
1272
  if (isLast) lastBlockRef.current = entry;
1249
- elements.push(jsx(Fragment, {
1250
- children: element
1251
- }, stableKey));
1273
+ elements.push(wrapBlockScope(element, stableKey, isLast && !!(options === null || options === void 0 ? void 0 : options.streaming)));
1252
1274
  }
1253
1275
  blockCacheRef.current = newCache;
1254
1276
  return jsxs(Fragment, {
@@ -1275,7 +1297,7 @@ export var useMarkdownToReact = function useMarkdownToReact(content, options) {
1275
1297
  var mdast = processor.parse(preprocessed);
1276
1298
  var hast = processor.runSync(mdast);
1277
1299
  var userComps = components || {};
1278
- var allComponents = buildEditorAlignedComponents('ant-agentic-md-editor', userComps, false);
1300
+ var allComponents = buildEditorAlignedComponents('ant-agentic-md-editor', userComps, false, undefined, false);
1279
1301
  return toJsxRuntime(hast, {
1280
1302
  Fragment: Fragment,
1281
1303
  jsx: jsx,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ant-design/agentic-ui",
3
- "version": "2.30.5",
3
+ "version": "2.30.6",
4
4
  "description": "面向智能体的 UI 组件库,提供多步推理可视化、工具调用展示、任务执行协同等 Agentic UI 能力",
5
5
  "repository": "git@github.com:ant-design/agentic-ui.git",
6
6
  "license": "MIT",
@@ -24,7 +24,6 @@
24
24
  "lint:css": "stylelint \"{src,test}/**/*.{css,less}\"",
25
25
  "lint:es": "eslint \"{src,test}/**/*.{js,jsx,ts,tsx}\"",
26
26
  "prepare": "husky install && dumi setup",
27
- "prepublishOnly": "npm run build && npm run test",
28
27
  "prettier": "prettier --write \"{src,docs,test}/**/*.{js,jsx,ts,tsx,css,less,json,md}\"",
29
28
  "preview": "pnpm dumi preview",
30
29
  "report:demo": "node scripts/generateDemoReport.js",