@ant-design/agentic-ui 2.23.0 → 2.24.1

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.
@@ -71,15 +71,15 @@ var canRenderThoughtChain = function(placement, role, thoughtChainEnabled) {
71
71
  if (thoughtChainEnabled === false) return false;
72
72
  return true;
73
73
  };
74
- /**
75
- * BubbleBeforeNode 组件
76
- *
77
- * 在聊天气泡之前渲染思维链或任务列表,显示AI的思考过程
78
- *
79
- * @example
80
- * ```tsx
81
- * <BubbleBeforeNode bubble={bubbleData} />
82
- * ```
74
+ /**
75
+ * BubbleBeforeNode 组件
76
+ *
77
+ * 在聊天气泡之前渲染思维链或任务列表,显示AI的思考过程
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * <BubbleBeforeNode bubble={bubbleData} />
82
+ * ```
83
83
  */ export var BubbleBeforeNode = function(param) {
84
84
  var bubble = param.bubble, className = param.className, style = param.style;
85
85
  var _context_thoughtChain, _originData_extra, _context_thoughtChain1, _context_thoughtChain2;
@@ -51,11 +51,13 @@ function _object_spread_props(target, source) {
51
51
  return target;
52
52
  }
53
53
  import SkeletonList from "./SkeletonList";
54
- import { useContext, useMemo } from "react";
54
+ import { useContext, useMemo, useRef } from "react";
55
55
  import { ConfigProvider } from "antd";
56
56
  import cx from "classnames";
57
+ import { nanoid } from "nanoid";
57
58
  import React from "react";
58
59
  import { BubbleConfigContext } from "../BubbleConfigProvide";
60
+ import { LOADING_FLAT } from "../MessagesContent";
59
61
  import { PureAIBubble, PureUserBubble } from "../PureBubble";
60
62
  import { useStyle } from "./style";
61
63
  export var PureBubbleList = function(props) {
@@ -71,6 +73,8 @@ export var PureBubbleList = function(props) {
71
73
  }, [
72
74
  JSON.stringify(props.style)
73
75
  ]);
76
+ // 为 loading 项生成唯一的 key,使用 ref 缓存以确保稳定性
77
+ var loadingKeysRef = useRef(new Map());
74
78
  var listDom = useMemo(function() {
75
79
  return bubbleList.map(function(item, index) {
76
80
  var placement = item.role === 'user' ? 'right' : 'left';
@@ -78,8 +82,18 @@ export var PureBubbleList = function(props) {
78
82
  var isLast = index === bubbleList.length - 1;
79
83
  item.isLatest = isLast;
80
84
  item.isLast = isLast;
85
+ // 如果 id 是 LOADING_FLAT,使用 uuid 作为 key
86
+ // 使用 index 和 createAt 的组合作为缓存 key,确保同一项在重新渲染时保持相同的 key
87
+ var itemKey = item.id;
88
+ if (item.id === LOADING_FLAT) {
89
+ var cacheKey = "".concat(index, "-").concat(item.createAt || Date.now());
90
+ if (!loadingKeysRef.current.has(cacheKey)) {
91
+ loadingKeysRef.current.set(cacheKey, nanoid());
92
+ }
93
+ itemKey = loadingKeysRef.current.get(cacheKey);
94
+ }
81
95
  return /*#__PURE__*/ React.createElement(BubbleComponent, {
82
- key: item.id,
96
+ key: itemKey,
83
97
  "data-id": item.id,
84
98
  avatar: _object_spread({}, placement === 'right' ? userMeta : assistantMeta, item.meta),
85
99
  preMessage: bubbleList[index - 1],
@@ -52,12 +52,14 @@ function _object_spread_props(target, source) {
52
52
  }
53
53
  import SkeletonList from "./SkeletonList";
54
54
  export { PureBubbleList } from "./PureBubbleList";
55
- import { useContext, useMemo } from "react";
55
+ import { useContext, useMemo, useRef } from "react";
56
56
  import { ConfigProvider } from "antd";
57
57
  import cx from "classnames";
58
+ import { nanoid } from "nanoid";
58
59
  import React from "react";
59
60
  import { Bubble } from "../Bubble";
60
61
  import { BubbleConfigContext } from "../BubbleConfigProvide";
62
+ import { LOADING_FLAT } from "../MessagesContent";
61
63
  import { useStyle } from "./style";
62
64
  /**
63
65
  * BubbleList 组件 - 聊天气泡列表组件
@@ -130,6 +132,9 @@ import { useStyle } from "./style";
130
132
  }, [
131
133
  JSON.stringify(props.style)
132
134
  ]);
135
+ // 为 loading 项生成唯一的 key,使用 ref 缓存以确保稳定性
136
+ // 使用 item 的唯一标识(index + createAt)作为缓存 key
137
+ var loadingKeysRef = useRef(new Map());
133
138
  var bubbleListDom = useMemo(function() {
134
139
  return bubbleList.map(function(item, index) {
135
140
  var _props_bubbleRenderConfig;
@@ -138,8 +143,18 @@ import { useStyle } from "./style";
138
143
  // 保持向后兼容性,设置isLatest
139
144
  item.isLatest = isLast;
140
145
  item.isLast = isLast;
146
+ // 如果 id 是 LOADING_FLAT,使用 uuid 作为 key
147
+ // 使用 index 和 createAt 的组合作为缓存 key,确保同一项在重新渲染时保持相同的 key
148
+ var itemKey = item.id;
149
+ if (item.id === LOADING_FLAT) {
150
+ var cacheKey = "".concat(index, "-").concat(item.createAt || Date.now());
151
+ if (!loadingKeysRef.current.has(cacheKey)) {
152
+ loadingKeysRef.current.set(cacheKey, nanoid());
153
+ }
154
+ itemKey = loadingKeysRef.current.get(cacheKey);
155
+ }
141
156
  return /*#__PURE__*/ React.createElement(Bubble, {
142
- key: item.id,
157
+ key: itemKey,
143
158
  "data-id": item.id,
144
159
  avatar: _object_spread({}, item.role === 'user' ? userMeta : assistantMeta, item.meta),
145
160
  preMessage: bubbleList[index - 1],
@@ -52,10 +52,11 @@ function _object_spread_props(target, source) {
52
52
  }
53
53
  import { ConfigProvider } from "antd";
54
54
  import classNames from "classnames";
55
- import React, { useContext } from "react";
55
+ import React, { useContext, useRef } from "react";
56
56
  import { Editor, Path, Transforms } from "slate";
57
57
  import { ReactEditor } from "slate-react";
58
58
  import { I18nContext } from "../../../I18n";
59
+ import { isMobileDevice } from "../../../MarkdownInputField/AttachmentButton/utils";
59
60
  import { useEditorStore } from "../store";
60
61
  import { EditorUtils } from "../utils/editorUtils";
61
62
  import { Blockquote } from "./Blockquote";
@@ -407,6 +408,46 @@ var MLeafComponent = function(props) {
407
408
  }
408
409
  } catch (e) {}
409
410
  };
411
+ var handleFncOpen = function() {
412
+ var _props_fncProps;
413
+ if ((_props_fncProps = props.fncProps) === null || _props_fncProps === void 0 ? void 0 : _props_fncProps.onOriginUrlClick) {
414
+ props.fncProps.onOriginUrlClick(leaf === null || leaf === void 0 ? void 0 : leaf.identifier);
415
+ }
416
+ };
417
+ var isMobile = isMobileDevice();
418
+ var hasFnc = leaf.fnc || leaf.identifier;
419
+ // 长按处理:用于手机端打开 fnc
420
+ var longPressTimerRef = useRef(null);
421
+ var touchStartTimeRef = useRef(0);
422
+ var isLongPressRef = useRef(false);
423
+ var handleTouchStart = function() {
424
+ if (!hasFnc) return;
425
+ isLongPressRef.current = false;
426
+ touchStartTimeRef.current = Date.now();
427
+ longPressTimerRef.current = setTimeout(function() {
428
+ isLongPressRef.current = true;
429
+ handleFncOpen();
430
+ }, 500); // 500ms 长按时间
431
+ };
432
+ var handleTouchEnd = function(e) {
433
+ if (!hasFnc) return;
434
+ if (longPressTimerRef.current) {
435
+ clearTimeout(longPressTimerRef.current);
436
+ longPressTimerRef.current = null;
437
+ }
438
+ // 如果是短按(小于 500ms),在手机上阻止默认行为
439
+ var touchDuration = Date.now() - touchStartTimeRef.current;
440
+ if (touchDuration < 500 && isMobile) {
441
+ e.preventDefault();
442
+ }
443
+ };
444
+ var handleTouchCancel = function() {
445
+ if (longPressTimerRef.current) {
446
+ clearTimeout(longPressTimerRef.current);
447
+ longPressTimerRef.current = null;
448
+ }
449
+ isLongPressRef.current = false;
450
+ };
410
451
  var _obj;
411
452
  var fncClassName = classNames(prefixClassName === null || prefixClassName === void 0 ? void 0 : prefixClassName.trim(), props.hashId, (_obj = {}, _define_property(_obj, "".concat(mdEditorBaseClass, "-fnc"), leaf.fnc), _define_property(_obj, "".concat(mdEditorBaseClass, "-fnd"), leaf.fnd), _define_property(_obj, "".concat(mdEditorBaseClass, "-comment"), leaf.comment), _obj));
412
453
  var dom = /*#__PURE__*/ React.createElement("span", _object_spread_props(_object_spread({}, props.attributes), {
@@ -415,6 +456,11 @@ var MLeafComponent = function(props) {
415
456
  onDragStart: dragStart,
416
457
  onClick: function(e) {
417
458
  var _props_fncProps;
459
+ // 在手机上,如果是 fnc,阻止点击事件(使用长按代替)
460
+ if (isMobile && hasFnc) {
461
+ e.preventDefault();
462
+ return;
463
+ }
418
464
  if (e.detail === 2) {
419
465
  selectFormat();
420
466
  }
@@ -422,6 +468,9 @@ var MLeafComponent = function(props) {
422
468
  props.fncProps.onOriginUrlClick(leaf === null || leaf === void 0 ? void 0 : leaf.identifier);
423
469
  }
424
470
  },
471
+ onTouchStart: hasFnc ? handleTouchStart : undefined,
472
+ onTouchEnd: hasFnc ? handleTouchEnd : undefined,
473
+ onTouchCancel: hasFnc ? handleTouchCancel : undefined,
425
474
  contentEditable: leaf.fnc ? false : undefined,
426
475
  "data-fnc": leaf.fnc || leaf.identifier ? 'fnc' : undefined,
427
476
  "data-fnd": leaf.fnd ? 'fnd' : undefined,
@@ -470,17 +470,31 @@ import partialJsonParse from "../json-parse";
470
470
  var isComment = commentValue.trim().startsWith('<!--') && commentValue.trim().endsWith('-->');
471
471
  // 检查是否是 otherProps 序列化生成的 JSON 注释
472
472
  // 这些注释应该被跳过,不应该被解析为 HTML 代码块
473
+ // 但是,如果注释包含图表配置(chartType),应该保留为 code 节点以便表格解析器使用
473
474
  if (isComment) {
474
475
  try {
476
+ var _parsed_;
475
477
  var commentContent = commentValue.replace('<!--', '').replace('-->', '').trim();
476
478
  var parsed = JSON.parse(commentContent);
477
- // 如果能够成功解析为 JSON 对象,且是对象类型(不是数组或基本类型),
478
- // 则认为是 otherProps 序列化生成的注释,应该返回 null 或空文本
479
- // 这些注释应该在 parserMarkdownToSlateNode 中被跳过
479
+ // 检查是否是图表配置
480
+ var isChartConfig = // 对象格式:{"chartType": ...}
481
+ (typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) === 'object' && parsed !== null && !Array.isArray(parsed) && parsed.chartType || // 数组格式:[{"chartType": ...}]
482
+ Array.isArray(parsed) && parsed.length > 0 && _type_of(parsed[0]) === 'object' && ((_parsed_ = parsed[0]) === null || _parsed_ === void 0 ? void 0 : _parsed_.chartType);
483
+ // 如果能够成功解析为 JSON 对象,且是对象类型(不是数组或基本类型)
484
+ // 注意:对象格式的图表配置已经在 handleHtml 中转换为数组格式了
485
+ // 这里只处理非图表配置的对象格式注释
480
486
  if ((typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) === 'object' && parsed !== null && !Array.isArray(parsed)) {
481
- return {
482
- text: ''
483
- };
487
+ // 如果包含 chartType 字段,说明是图表配置,应该已经在 handleHtml 中转换为数组格式
488
+ // 这里不应该再匹配到对象格式的图表配置,继续正常处理
489
+ if (isChartConfig && parsed.chartType) {
490
+ // 继续正常处理,创建 code 节点(此时应该已经是数组格式了)
491
+ } else {
492
+ // 否则认为是 otherProps 序列化生成的注释,应该返回空文本
493
+ // 这些注释应该在 parserMarkdownToSlateNode 中被跳过
494
+ return {
495
+ text: ''
496
+ };
497
+ }
484
498
  }
485
499
  } catch (e) {
486
500
  // 解析失败,不是 JSON 格式的注释,继续正常处理
@@ -704,6 +718,26 @@ import partialJsonParse from "../json-parse";
704
718
  var trimmedValue = (currentElement === null || currentElement === void 0 ? void 0 : (_currentElement_value = currentElement.value) === null || _currentElement_value === void 0 ? void 0 : _currentElement_value.trim()) || '';
705
719
  var isUnclosedComment = trimmedValue.startsWith('<!--') && !trimmedValue.endsWith('-->');
706
720
  var processedValue = isUnclosedComment ? trimmedValue + '-->' : (currentElement === null || currentElement === void 0 ? void 0 : currentElement.value) || '';
721
+ // 检查是否是对象格式的图表配置,如果是则转换为数组格式
722
+ var isComment = processedValue.trim().startsWith('<!--') && processedValue.trim().endsWith('-->');
723
+ if (isComment) {
724
+ try {
725
+ var commentContent = processedValue.replace('<!--', '').replace('-->', '').trim();
726
+ var parsed = JSON.parse(commentContent);
727
+ // 如果是对象格式且包含 chartType,转换为数组格式
728
+ if ((typeof parsed === "undefined" ? "undefined" : _type_of(parsed)) === 'object' && parsed !== null && !Array.isArray(parsed) && parsed.chartType) {
729
+ var arrayFormat = [
730
+ parsed
731
+ ];
732
+ var convertedContent = JSON.stringify(arrayFormat);
733
+ processedValue = "<!--".concat(convertedContent, "-->");
734
+ // 同时更新 currentElement.value,以便后续处理使用
735
+ currentElement.value = processedValue;
736
+ }
737
+ } catch (e) {
738
+ // 解析失败,不是 JSON 格式的注释,继续正常处理
739
+ }
740
+ }
707
741
  var value = (processedValue === null || processedValue === void 0 ? void 0 : processedValue.replace('<!--', '').replace('-->', '').trim()) || '{}';
708
742
  var contextProps = parseCommentContextProps(value, processedValue);
709
743
  var isBlockLevel = !parent || [
@@ -150,11 +150,7 @@ var myRemark = {
150
150
  */ export var parseTableOrChart = function(table, preNode, plugins, parseNodes, parserConfig) {
151
151
  var _table_children, _tableHeader_children, _table_children_slice, _table_children1, _table_align, _chartConfig_, _config_at, _config_at1;
152
152
  var keyMap = new Map();
153
- // @ts-ignore
154
- var config = // @ts-ignore
155
- (preNode === null || preNode === void 0 ? void 0 : preNode.type) === 'code' && // @ts-ignore
156
- (preNode === null || preNode === void 0 ? void 0 : preNode.language) === 'html' && (// @ts-ignore
157
- preNode === null || preNode === void 0 ? void 0 : preNode.otherProps) ? preNode === null || preNode === void 0 ? void 0 : preNode.otherProps : {};
153
+ var config = (preNode === null || preNode === void 0 ? void 0 : preNode.type) === 'code' && (preNode === null || preNode === void 0 ? void 0 : preNode.language) === 'html' && (preNode === null || preNode === void 0 ? void 0 : preNode.otherProps) ? preNode === null || preNode === void 0 ? void 0 : preNode.otherProps : parserConfig || {};
158
154
  var tableHeader = table === null || table === void 0 ? void 0 : (_table_children = table.children) === null || _table_children === void 0 ? void 0 : _table_children.at(0);
159
155
  var columns = (tableHeader === null || tableHeader === void 0 ? void 0 : (_tableHeader_children = tableHeader.children) === null || _tableHeader_children === void 0 ? void 0 : _tableHeader_children.map(function(node) {
160
156
  var _myRemark_stringify;
@@ -239,7 +235,7 @@ var myRemark = {
239
235
  chartConfig = convertObjectToArray(chartConfig);
240
236
  var isChart = (chartConfig === null || chartConfig === void 0 ? void 0 : chartConfig.chartType) || Array.isArray(chartConfig) && (chartConfig === null || chartConfig === void 0 ? void 0 : (_chartConfig_ = chartConfig[0]) === null || _chartConfig_ === void 0 ? void 0 : _chartConfig_.chartType) || (config === null || config === void 0 ? void 0 : config.chartType) || (config === null || config === void 0 ? void 0 : (_config_at1 = config.at) === null || _config_at1 === void 0 ? void 0 : (_config_at = _config_at1.call(config, 0)) === null || _config_at === void 0 ? void 0 : _config_at.chartType);
241
237
  // 计算合并单元格信息
242
- var mergeCells = config.mergeCells || [];
238
+ var mergeCells = (config === null || config === void 0 ? void 0 : config.mergeCells) || [];
243
239
  // 创建合并单元格映射,用于快速查找
244
240
  var mergeMap = new Map();
245
241
  mergeCells === null || mergeCells === void 0 ? void 0 : mergeCells.forEach(function(param) {
@@ -0,0 +1,53 @@
1
+ import { Elements } from '../../el';
2
+ /**
3
+ * Markdown 解析缓存类
4
+ *
5
+ * 使用 Map 存储 markdown 块到 schema 的映射,避免重复解析相同的 markdown 内容
6
+ */
7
+ export declare class ParseCache {
8
+ private cache;
9
+ /**
10
+ * 从缓存中获取解析结果
11
+ *
12
+ * @param md - markdown 字符串
13
+ * @returns 如果缓存中存在则返回解析结果,否则返回 null
14
+ */
15
+ get(md: string): Elements[] | null;
16
+ /**
17
+ * 将解析结果存入缓存
18
+ *
19
+ * @param md - markdown 字符串
20
+ * @param schema - 解析后的 schema 数组
21
+ */
22
+ set(md: string, schema: Elements[]): void;
23
+ /**
24
+ * 检查缓存中是否存在指定的 markdown
25
+ *
26
+ * @param md - markdown 字符串
27
+ * @returns 是否存在
28
+ */
29
+ has(md: string): boolean;
30
+ /**
31
+ * 清空缓存
32
+ */
33
+ clear(): void;
34
+ /**
35
+ * 获取缓存大小
36
+ *
37
+ * @returns 缓存中存储的条目数
38
+ */
39
+ size(): number;
40
+ }
41
+ /**
42
+ * 按 \n\n 切分 markdown,但保护不应被切分的结构
43
+ *
44
+ * 切分规则:
45
+ * 1. 按 \n\n(双换行)切分 markdown
46
+ * 2. 保护代码块(```code```):代码块内部的 \n\n 不作为分隔符
47
+ * 3. 保护 HTML 注释(<!-- -->):注释内部的 \n\n 不作为分隔符
48
+ * 4. 保护 HTML 标签(<tag>...</tag>):标签内部的 \n\n 不作为分隔符
49
+ *
50
+ * @param md - 要切分的 markdown 字符串
51
+ * @returns 切分后的 markdown 块数组
52
+ */
53
+ export declare function splitMarkdownIntoBlocks(md: string): string[];
@@ -0,0 +1,355 @@
1
+ function _class_call_check(instance, Constructor) {
2
+ if (!(instance instanceof Constructor)) {
3
+ throw new TypeError("Cannot call a class as a function");
4
+ }
5
+ }
6
+ function _defineProperties(target, props) {
7
+ for(var i = 0; i < props.length; i++){
8
+ var descriptor = props[i];
9
+ descriptor.enumerable = descriptor.enumerable || false;
10
+ descriptor.configurable = true;
11
+ if ("value" in descriptor) descriptor.writable = true;
12
+ Object.defineProperty(target, descriptor.key, descriptor);
13
+ }
14
+ }
15
+ function _create_class(Constructor, protoProps, staticProps) {
16
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
17
+ if (staticProps) _defineProperties(Constructor, staticProps);
18
+ return Constructor;
19
+ }
20
+ function _define_property(obj, key, value) {
21
+ if (key in obj) {
22
+ Object.defineProperty(obj, key, {
23
+ value: value,
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true
27
+ });
28
+ } else {
29
+ obj[key] = value;
30
+ }
31
+ return obj;
32
+ }
33
+ /**
34
+ * Markdown 解析缓存类
35
+ *
36
+ * 使用 Map 存储 markdown 块到 schema 的映射,避免重复解析相同的 markdown 内容
37
+ */ export var ParseCache = /*#__PURE__*/ function() {
38
+ "use strict";
39
+ function ParseCache() {
40
+ _class_call_check(this, ParseCache);
41
+ _define_property(this, "cache", new Map());
42
+ }
43
+ _create_class(ParseCache, [
44
+ {
45
+ /**
46
+ * 从缓存中获取解析结果
47
+ *
48
+ * @param md - markdown 字符串
49
+ * @returns 如果缓存中存在则返回解析结果,否则返回 null
50
+ */ key: "get",
51
+ value: function get(md) {
52
+ return this.cache.get(md) || null;
53
+ }
54
+ },
55
+ {
56
+ /**
57
+ * 将解析结果存入缓存
58
+ *
59
+ * @param md - markdown 字符串
60
+ * @param schema - 解析后的 schema 数组
61
+ */ key: "set",
62
+ value: function set(md, schema) {
63
+ this.cache.set(md, schema);
64
+ }
65
+ },
66
+ {
67
+ /**
68
+ * 检查缓存中是否存在指定的 markdown
69
+ *
70
+ * @param md - markdown 字符串
71
+ * @returns 是否存在
72
+ */ key: "has",
73
+ value: function has(md) {
74
+ return this.cache.has(md);
75
+ }
76
+ },
77
+ {
78
+ /**
79
+ * 清空缓存
80
+ */ key: "clear",
81
+ value: function clear() {
82
+ this.cache.clear();
83
+ }
84
+ },
85
+ {
86
+ /**
87
+ * 获取缓存大小
88
+ *
89
+ * @returns 缓存中存储的条目数
90
+ */ key: "size",
91
+ value: function size() {
92
+ return this.cache.size;
93
+ }
94
+ }
95
+ ]);
96
+ return ParseCache;
97
+ }();
98
+ /**
99
+ * 获取代码块围栏的长度
100
+ */ function getFenceLength(md, start, fenceChar) {
101
+ var length = 0;
102
+ while(start + length < md.length && md[start + length] === fenceChar){
103
+ length++;
104
+ }
105
+ return length;
106
+ }
107
+ /**
108
+ * 查找 HTML 注释的结束位置
109
+ * 返回结束位置(包含 -->),如果未找到则返回 -1
110
+ */ function findHtmlCommentEnd(md, start) {
111
+ if (md.slice(start, start + 4) !== '<!--') {
112
+ return -1;
113
+ }
114
+ for(var i = start + 4; i < md.length - 2; i++){
115
+ if (md.slice(i, i + 3) === '-->') {
116
+ return i + 3;
117
+ }
118
+ }
119
+ return -1;
120
+ }
121
+ /**
122
+ * 查找 HTML 标签信息
123
+ * 返回标签信息对象,如果未找到则返回 null
124
+ */ function findHtmlTagInfo(md, start) {
125
+ if (md[start] !== '<') {
126
+ return null;
127
+ }
128
+ var i = start + 1;
129
+ // 跳过可能的 /(结束标签)
130
+ if (i < md.length && md[i] === '/') {
131
+ return null;
132
+ }
133
+ // 提取标签名
134
+ var tagName = '';
135
+ while(i < md.length && /[a-zA-Z0-9-]/.test(md[i])){
136
+ tagName += md[i];
137
+ i++;
138
+ }
139
+ if (!tagName) {
140
+ return null;
141
+ }
142
+ // 跳过空白和属性,查找 > 或 />
143
+ // 需要处理属性值中的引号
144
+ var inQuotes = false;
145
+ var quoteChar = '';
146
+ while(i < md.length){
147
+ var char = md[i];
148
+ // 处理引号(检查前一个字符是否是转义字符)
149
+ if (char === '"' || char === "'") {
150
+ var prevChar = i > 0 ? md[i - 1] : '';
151
+ if (prevChar !== '\\') {
152
+ if (!inQuotes) {
153
+ inQuotes = true;
154
+ quoteChar = char;
155
+ } else if (char === quoteChar) {
156
+ inQuotes = false;
157
+ quoteChar = '';
158
+ }
159
+ }
160
+ }
161
+ // 只有在引号外部才检查标签结束
162
+ if (!inQuotes) {
163
+ if (char === '>') {
164
+ return {
165
+ name: tagName.toLowerCase(),
166
+ end: i + 1,
167
+ isSelfClosing: false
168
+ };
169
+ }
170
+ if (i < md.length - 1 && md.slice(i, i + 2) === '/>') {
171
+ return {
172
+ name: tagName.toLowerCase(),
173
+ end: i + 2,
174
+ isSelfClosing: true
175
+ };
176
+ }
177
+ }
178
+ i++;
179
+ }
180
+ return null;
181
+ }
182
+ /**
183
+ * 查找 HTML 结束标签的位置
184
+ * 返回结束位置(包含 >),如果未找到则返回 -1
185
+ */ function findHtmlClosingTagEnd(md, start, tagName) {
186
+ if (md.slice(start, start + 2) !== '</') {
187
+ return -1;
188
+ }
189
+ // 检查标签名是否匹配
190
+ var expectedTag = "</".concat(tagName);
191
+ if (md.slice(start, start + expectedTag.length).toLowerCase() !== expectedTag.toLowerCase()) {
192
+ return -1;
193
+ }
194
+ // 查找 >
195
+ var i = start + expectedTag.length;
196
+ while(i < md.length && /\s/.test(md[i])){
197
+ i++;
198
+ }
199
+ if (i < md.length && md[i] === '>') {
200
+ return i + 1;
201
+ }
202
+ return -1;
203
+ }
204
+ /**
205
+ * 检查是否应该保护此分隔符(不切分)
206
+ * 返回 true 如果:
207
+ * 1. HTML 注释后紧跟着表格行
208
+ * 2. 当前块包含表格行,且后面也是表格行
209
+ */ function shouldProtectSeparator(md, separatorIndex, currentBlock) {
210
+ // 跳过 \n\n 和后续的换行符,查找下一行的开始
211
+ var nextLineStart = separatorIndex + 2;
212
+ while(nextLineStart < md.length && md[nextLineStart] === '\n'){
213
+ nextLineStart++;
214
+ }
215
+ // 检查下一行是否以 | 开头(表格行)
216
+ if (nextLineStart >= md.length || md[nextLineStart] !== '|') {
217
+ return false;
218
+ }
219
+ // 情况1:当前块以 HTML 注释结尾,后面是表格行
220
+ var trimmedBlock = currentBlock.trim();
221
+ if (trimmedBlock.endsWith('-->')) {
222
+ var commentStart = trimmedBlock.lastIndexOf('<!--');
223
+ if (commentStart !== -1) {
224
+ return true;
225
+ }
226
+ }
227
+ // 情况2:当前块包含表格行(检查最后几行是否包含表格行)
228
+ var lines = currentBlock.split('\n');
229
+ for(var i = lines.length - 1; i >= Math.max(0, lines.length - 5); i--){
230
+ var line = lines[i].trim();
231
+ if (line.startsWith('|') && line.endsWith('|')) {
232
+ return true;
233
+ }
234
+ }
235
+ return false;
236
+ }
237
+ /**
238
+ * 按 \n\n 切分 markdown,但保护不应被切分的结构
239
+ *
240
+ * 切分规则:
241
+ * 1. 按 \n\n(双换行)切分 markdown
242
+ * 2. 保护代码块(```code```):代码块内部的 \n\n 不作为分隔符
243
+ * 3. 保护 HTML 注释(<!-- -->):注释内部的 \n\n 不作为分隔符
244
+ * 4. 保护 HTML 标签(<tag>...</tag>):标签内部的 \n\n 不作为分隔符
245
+ *
246
+ * @param md - 要切分的 markdown 字符串
247
+ * @returns 切分后的 markdown 块数组
248
+ */ export function splitMarkdownIntoBlocks(md) {
249
+ if (!md) {
250
+ return [];
251
+ }
252
+ var blocks = [];
253
+ var currentBlock = '';
254
+ var i = 0;
255
+ var inCodeBlock = false;
256
+ var codeBlockFence = '';
257
+ var inHtmlTag = false;
258
+ var htmlTagName = '';
259
+ while(i < md.length){
260
+ var char = md[i];
261
+ var nextChar = i + 1 < md.length ? md[i + 1] : null;
262
+ // 检测代码块开始:``` 或 ~~~
263
+ if (!inCodeBlock && !inHtmlTag && (char === '`' || char === '~')) {
264
+ var fenceLength = getFenceLength(md, i, char);
265
+ if (fenceLength >= 3) {
266
+ inCodeBlock = true;
267
+ codeBlockFence = char.repeat(fenceLength);
268
+ currentBlock += codeBlockFence;
269
+ i += fenceLength;
270
+ continue;
271
+ }
272
+ }
273
+ // 检测代码块结束
274
+ if (inCodeBlock && md.slice(i, i + codeBlockFence.length) === codeBlockFence) {
275
+ currentBlock += codeBlockFence;
276
+ i += codeBlockFence.length;
277
+ inCodeBlock = false;
278
+ codeBlockFence = '';
279
+ continue;
280
+ }
281
+ // 检测 HTML 注释开始:<!--
282
+ if (!inCodeBlock && !inHtmlTag && md.slice(i, i + 4) === '<!--') {
283
+ var commentEnd = findHtmlCommentEnd(md, i);
284
+ if (commentEnd > i) {
285
+ currentBlock += md.slice(i, commentEnd);
286
+ i = commentEnd;
287
+ continue;
288
+ }
289
+ }
290
+ // 检测 HTML 标签开始:<tag
291
+ if (!inCodeBlock && !inHtmlTag && char === '<') {
292
+ var tagInfo = findHtmlTagInfo(md, i);
293
+ if (tagInfo) {
294
+ if (tagInfo.isSelfClosing) {
295
+ // 自闭合标签,直接添加并跳过
296
+ currentBlock += md.slice(i, tagInfo.end);
297
+ i = tagInfo.end;
298
+ continue;
299
+ } else {
300
+ // 开始标签,查找对应的结束标签
301
+ inHtmlTag = true;
302
+ htmlTagName = tagInfo.name;
303
+ currentBlock += md.slice(i, tagInfo.end);
304
+ i = tagInfo.end;
305
+ continue;
306
+ }
307
+ }
308
+ }
309
+ // 检测 HTML 标签结束:</tag>
310
+ if (inHtmlTag && md.slice(i, i + 2) === '</') {
311
+ var closingTagEnd = findHtmlClosingTagEnd(md, i, htmlTagName);
312
+ if (closingTagEnd > i) {
313
+ currentBlock += md.slice(i, closingTagEnd);
314
+ i = closingTagEnd;
315
+ inHtmlTag = false;
316
+ htmlTagName = '';
317
+ continue;
318
+ }
319
+ }
320
+ // 检测块分隔符 \n\n(仅在代码块、HTML 注释和 HTML 标签外部)
321
+ if (!inCodeBlock && !inHtmlTag && char === '\n' && nextChar === '\n') {
322
+ // 检查是否应该保护此分隔符(HTML 注释后跟着表格,或表格行之间)
323
+ if (shouldProtectSeparator(md, i, currentBlock)) {
324
+ // 不切分,继续添加到当前块
325
+ currentBlock += '\n\n';
326
+ i += 2;
327
+ while(i < md.length && md[i] === '\n'){
328
+ currentBlock += '\n';
329
+ i++;
330
+ }
331
+ continue;
332
+ }
333
+ if (currentBlock.trim().length > 0) {
334
+ blocks.push(currentBlock.trim());
335
+ }
336
+ currentBlock = '';
337
+ // 跳过连续的换行符
338
+ i += 2;
339
+ while(i < md.length && md[i] === '\n'){
340
+ i++;
341
+ }
342
+ continue;
343
+ }
344
+ // 普通字符,添加到当前块
345
+ currentBlock += char;
346
+ i++;
347
+ }
348
+ // 处理最后一个块
349
+ if (currentBlock.trim().length > 0) {
350
+ blocks.push(currentBlock.trim());
351
+ }
352
+ return blocks.filter(function(block) {
353
+ return block.trim().length > 0;
354
+ });
355
+ }
@@ -1,4 +1,4 @@
1
- import { Elements } from '../../el';
1
+ import { ChartTypeConfig, Elements } from '../../el';
2
2
  import { MarkdownEditorPlugin } from '../../plugin';
3
3
  /**
4
4
  * 解析Markdown字符串的配置选项
@@ -10,6 +10,7 @@ export interface ParserMarkdownToSlateNodeConfig {
10
10
  paragraphTag?: string;
11
11
  /** 是否正在输入中(打字机模式) */
12
12
  typing?: boolean;
13
+ config?: ChartTypeConfig[];
13
14
  }
14
15
  /**
15
16
  * Markdown 到 Slate 节点解析器类
@@ -18,7 +19,7 @@ export interface ParserMarkdownToSlateNodeConfig {
18
19
  * 使用类形式可以避免在函数调用链中传递配置参数和插件。
19
20
  */
20
21
  export declare class MarkdownToSlateParser {
21
- private readonly config;
22
+ private config;
22
23
  private readonly plugins;
23
24
  constructor(config?: ParserMarkdownToSlateNodeConfig, plugins?: MarkdownEditorPlugin[]);
24
25
  /**
@@ -162,6 +162,11 @@ import mdastParser from "./remarkParse";
162
162
  // 对齐注释(如 {"align":"center"})不包含这些属性,应该被保留
163
163
  isOtherPropsComment = hasCodeMetadataProps;
164
164
  }
165
+ if (Array.isArray(htmlCommentProps)) {
166
+ htmlCommentProps = {
167
+ config: htmlCommentProps
168
+ };
169
+ }
165
170
  } catch (e) {
166
171
  // 解析失败,不是 JSON 格式的注释,可能是真正的 HTML 注释
167
172
  isOtherPropsComment = false;
@@ -237,6 +242,9 @@ import mdastParser from "./remarkParse";
237
242
  }
238
243
  }
239
244
  }
245
+ if (Object.keys(config).length > 0) {
246
+ _this.config = _object_spread({}, _this.config, config);
247
+ }
240
248
  // 如果插件没有处理,使用默认处理逻辑
241
249
  if (!pluginHandled) {
242
250
  // 使用统一的处理函数,通过 this 访问配置和插件
@@ -305,7 +305,7 @@ import { getPointStrOffset, getSelectionFromDomSelection } from "../../utils/edi
305
305
  key: "comment",
306
306
  className: classnames("".concat(baseClassName, "-item"), hashId),
307
307
  onClick: function() {
308
- var _i18n_locale;
308
+ var _i18n_locale, _editorProps_comment, _i18n_locale1;
309
309
  if (typeof window === 'undefined') return;
310
310
  var domSelection = window.getSelection();
311
311
  var editor = markdownEditorRef.current;
@@ -376,6 +376,7 @@ import { getPointStrOffset, getSelectionFromDomSelection } from "../../utils/edi
376
376
  height: 100,
377
377
  resize: 'none'
378
378
  },
379
+ placeholder: (editorProps === null || editorProps === void 0 ? void 0 : (_editorProps_comment = editorProps.comment) === null || _editorProps_comment === void 0 ? void 0 : _editorProps_comment.placeholder) || (editorProps === null || editorProps === void 0 ? void 0 : editorProps.titlePlaceholderContent) || ((_i18n_locale1 = i18n.locale) === null || _i18n_locale1 === void 0 ? void 0 : _i18n_locale1.inputPlaceholder) || '请输入内容...',
379
380
  onChange: function(e) {
380
381
  comment.content = e.target.value;
381
382
  }
@@ -17,7 +17,14 @@ export type CodeNode<T = Record<string, any>> = {
17
17
  className?: string;
18
18
  language?: string;
19
19
  render?: boolean;
20
+ mergeCells?: Array<{
21
+ row: number;
22
+ col: number;
23
+ rowSpan: number;
24
+ colSpan: number;
25
+ }>;
20
26
  frontmatter?: boolean;
27
+ config?: Record<string, any>[];
21
28
  } & T;
22
29
  children: [{
23
30
  text: string;
@@ -280,6 +280,11 @@ export type MarkdownEditorProps = {
280
280
  onEdit?: (id: string | number, comment: CommentDataType) => void;
281
281
  deleteConfirmText?: string;
282
282
  mentionsPlaceholder?: string;
283
+ /**
284
+ * 评论输入框占位符
285
+ * @description 评论输入框的占位符文本,如果不提供则使用 titlePlaceholderContent
286
+ */
287
+ placeholder?: string;
283
288
  listItemRender?: (defaultDom: {
284
289
  checkbox: React.JSX.Element | null;
285
290
  mentionsUser: React.JSX.Element | null;
@@ -254,6 +254,7 @@ import React, { useCallback, useContext, useEffect, useImperativeHandle, useMemo
254
254
  import { useRefFunction } from "../Hooks/useRefFunction";
255
255
  import { BaseMarkdownEditor } from "../MarkdownEditor";
256
256
  import { upLoadFileToServer } from "./AttachmentButton";
257
+ import { isMobileDevice } from "./AttachmentButton/utils";
257
258
  import { AttachmentFileList } from "./AttachmentButton/AttachmentFileList";
258
259
  import { getFileListFromDataTransferItems } from "./FilePaste";
259
260
  import { useFileUploadManager } from "./FileUploadManager";
@@ -642,6 +643,10 @@ import { useVoiceInputManager } from "./VoiceInputManager";
642
643
  var isEnter = e.key === 'Enter';
643
644
  var isMod = e.ctrlKey || e.metaKey;
644
645
  var isShift = e.shiftKey;
646
+ // 手机端禁用 Enter 键发送
647
+ if (isEnter && !isMod && !isShift && isMobileDevice()) {
648
+ return; // 让编辑器正常处理换行
649
+ }
645
650
  // Enter 发送,Shift+Enter 换行
646
651
  if (!isEnter || isMod) return;
647
652
  if (isShift) return; // Shift+Enter 时让编辑器处理换行
@@ -3,8 +3,8 @@
3
3
  * 提供图表组件相关的 React Hooks
4
4
  * @author Chart Plugin Team
5
5
  */
6
- export type { ChartStatisticConfig, StatisticConfigType } from './useChartStatistic';
7
6
  export { useChartDataFilter } from './useChartDataFilter';
7
+ export type { ChartStatisticConfig, StatisticConfigType, } from './useChartStatistic';
8
8
  export { useChartStatistics } from './useChartStatistics';
9
9
  export { useChartTheme } from './useChartTheme';
10
10
  export { useResponsiveSize } from './useResponsiveSize';
@@ -304,4 +304,4 @@ export declare const isConfigEqual: (config1: any, config2: any) => boolean;
304
304
  * @since 1.0.0
305
305
  */
306
306
  export declare const hexToRgba: (hex: string, alpha: number) => string;
307
- export { registerChartComponents, registerLineChartComponents, registerBarChartComponents, } from './utils/registerChart';
307
+ export { registerBarChartComponents, registerChartComponents, registerLineChartComponents, } from './utils/registerChart';
@@ -462,4 +462,4 @@ var intl = new Intl.NumberFormat('en-US', {
462
462
  return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(a, ")");
463
463
  };
464
464
  // 导出 Chart.js 注册相关函数
465
- export { registerChartComponents, registerLineChartComponents, registerBarChartComponents } from "./utils/registerChart";
465
+ export { registerBarChartComponents, registerChartComponents, registerLineChartComponents } from "./utils/registerChart";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ant-design/agentic-ui",
3
- "version": "2.23.0",
3
+ "version": "2.24.1",
4
4
  "description": "面向智能体的 UI 组件库,提供多步推理可视化、工具调用展示、任务执行协同等 Agentic UI 能力",
5
5
  "repository": "git@github.com:ant-design/agentic-ui.git",
6
6
  "license": "MIT",