@ant-design/agentic-ui 2.29.37 → 2.29.38

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.
@@ -132,9 +132,23 @@ import { Paragraph } from "./Paragraph";
132
132
  import { ReadonlyParagraph } from "./Paragraph/ReadonlyParagraph";
133
133
  import { Schema } from "./Schema";
134
134
  import { ReadonlySchema } from "./Schema/ReadonlySchema";
135
+ import { JINJA_DOLLAR_PLACEHOLDER } from "../parser/constants";
135
136
  import { tableRenderElement } from "./Table";
136
137
  import { ReadonlyTableComponent } from "./Table/ReadonlyTableComponent";
137
138
  import { TagPopup } from "./TagPopup";
139
+ /** 递归将 Jinja 占位符还原为 $ 显示 */ var restoreJinjaDollarInChildren = function restoreJinjaDollarInChildren1(children) {
140
+ return React.Children.map(children, function(child) {
141
+ if (typeof child === 'string') {
142
+ return child.split(JINJA_DOLLAR_PLACEHOLDER).join('$');
143
+ }
144
+ if (/*#__PURE__*/ React.isValidElement(child) && child.props.children !== undefined && child.props.children !== null) {
145
+ return /*#__PURE__*/ React.cloneElement(child, {
146
+ children: restoreJinjaDollarInChildren(child.props.children)
147
+ });
148
+ }
149
+ return child;
150
+ });
151
+ };
138
152
  /**
139
153
  * 性能优化说明:
140
154
  *
@@ -481,7 +495,7 @@ var MLeafComponent = function MLeafComponent(props) {
481
495
  "data-url": leaf.url ? 'url' : undefined,
482
496
  style: style,
483
497
  className: (prefixClassName === null || prefixClassName === void 0 ? void 0 : prefixClassName.trim()) ? prefixClassName === null || prefixClassName === void 0 ? void 0 : prefixClassName.trim() : undefined
484
- }), children);
498
+ }), restoreJinjaDollarInChildren(children));
485
499
  // 如果有评论,使用 CommentLeaf 包裹普通 DOM
486
500
  if (hasComment) {
487
501
  return /*#__PURE__*/ React.createElement(CommentLeaf, {
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 占位符:保护 Jinja 块内 $ 不被 remark-math 解析为数学公式。
3
+ * 在 markdown 解析前替换,渲染与序列化时还原为 $。
4
+ */
5
+ export declare const JINJA_DOLLAR_PLACEHOLDER = "\uE01A";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 占位符:保护 Jinja 块内 $ 不被 remark-math 解析为数学公式。
3
+ * 在 markdown 解析前替换,渲染与序列化时还原为 $。
4
+ */ export var JINJA_DOLLAR_PLACEHOLDER = '\uE01A';
@@ -82,6 +82,7 @@ function _unsupported_iterable_to_array(o, minLen) {
82
82
  /* eslint-disable @typescript-eslint/no-loop-func */ /* eslint-disable no-case-declarations */ /* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/no-use-before-define */ import { Node } from "slate";
83
83
  import stringWidth from "string-width";
84
84
  import { debugInfo } from "../../../Utils/debugUtils";
85
+ import { JINJA_DOLLAR_PLACEHOLDER } from "./constants";
85
86
  import { getMediaType } from "../utils/dom";
86
87
  var inlineNode = new Set([
87
88
  'break'
@@ -718,7 +719,7 @@ export var isMix = function isMix(t) {
718
719
  * - `<del>` if `strikethrough` is true.
719
720
  * - `<a href="{url}">` if `url` is defined.
720
721
  */ var textHtml = function textHtml(t) {
721
- var str = t.text || '';
722
+ var str = (t.text || '').split(JINJA_DOLLAR_PLACEHOLDER).join('$');
722
723
  if (t.highColor) str = '<span style="color:'.concat(t.highColor, '">').concat(str, "</span>");
723
724
  if (t.code) str = "<code>".concat(str, "</code>");
724
725
  if (t.italic) str = "<i>".concat(str, "</i>");
@@ -745,9 +746,9 @@ export var isMix = function isMix(t) {
745
746
  * - 将换行符转换为 Markdown 换行符
746
747
  * - 保留文本前后的空白字符
747
748
  */ var textStyle = function textStyle(t) {
748
- var _t_text;
749
+ var _split_join;
749
750
  if (!t.text && !t.tag) return '';
750
- var str = (t === null || t === void 0 ? void 0 : (_t_text = t.text) === null || _t_text === void 0 ? void 0 : _t_text.replace(RegExp("(?<!\\\\)\\\\", "g"), '\\').replace(/\n/g, ' \n')) || '';
751
+ var str = ((_split_join = ((t === null || t === void 0 ? void 0 : t.text) || '').split(JINJA_DOLLAR_PLACEHOLDER).join('$')) === null || _split_join === void 0 ? void 0 : _split_join.replace(RegExp("(?<!\\\\)\\\\", "g"), '\\').replace(/\n/g, ' \n')) || '';
751
752
  var preStr = '', afterStr = '';
752
753
  // Apply formats in a consistent order:
753
754
  // 1. Code (most specific)
@@ -834,7 +835,7 @@ export var isMix = function isMix(t) {
834
835
  });
835
836
  var str = textStyle(t);
836
837
  if (t === null || t === void 0 ? void 0 : t.url) {
837
- str = "[".concat(t.text, "](").concat(encodeURI(t === null || t === void 0 ? void 0 : t.url), ")");
838
+ str = "[".concat((t.text || '').split(JINJA_DOLLAR_PLACEHOLDER).join('$'), "](").concat(encodeURI(t === null || t === void 0 ? void 0 : t.url), ")");
838
839
  } else if (isMix(t) && index !== -1) {
839
840
  var next = siblings[index + 1];
840
841
  if (!str.endsWith(' ') && next && !Node.string(next).startsWith(' ')) {
@@ -15,5 +15,10 @@ export declare function convertParagraphToImage(): (tree: any) => void;
15
15
  * Transformer function.
16
16
  */
17
17
  export declare function fixStrongWithSpecialChars(): (tree: any) => void;
18
+ /**
19
+ * 保护 Jinja 块内的 $,防止被 remark-math 误解析为行内数学(如 {{ $var }})
20
+ * 须在 remark-math 之前运行
21
+ */
22
+ export declare function protectJinjaDollarInText(): (tree: any) => void;
18
23
  declare const markdownParser: import("unified").Processor<import("mdast").Root, undefined, undefined, import("mdast").Root, string>;
19
24
  export default markdownParser;
@@ -30,6 +30,7 @@ import remarkMath from "remark-math";
30
30
  import remarkParse from "remark-parse";
31
31
  import { unified } from "unified";
32
32
  import { visit } from "unist-util-visit";
33
+ import { JINJA_DOLLAR_PLACEHOLDER } from "./constants";
33
34
  /**
34
35
  * 提取段落节点的文本内容
35
36
  * @param paragraphNode - 段落节点
@@ -344,6 +345,20 @@ import { visit } from "unist-util-visit";
344
345
  });
345
346
  };
346
347
  }
348
+ /**
349
+ * 保护 Jinja 块内的 $,防止被 remark-math 误解析为行内数学(如 {{ $var }})
350
+ * 须在 remark-math 之前运行
351
+ */ export function protectJinjaDollarInText() {
352
+ return function(tree) {
353
+ visit(tree, 'text', function(node) {
354
+ if (!node.value || typeof node.value !== 'string') return;
355
+ var replaceInBlock = function replaceInBlock(str) {
356
+ return str.replace(/\$/g, JINJA_DOLLAR_PLACEHOLDER);
357
+ };
358
+ node.value = node.value.replace(/\{\{[^}]*\}\}/g, replaceInBlock).replace(/\{%[^%]*%\}/g, replaceInBlock).replace(/\{#[\s\S]*?#\}/g, replaceInBlock);
359
+ });
360
+ };
361
+ }
347
362
  // Markdown 解析器(用于解析 Markdown 为 mdast AST)
348
363
  // 注意:这个解析器只用于解析,不包含 HTML 渲染相关的插件
349
364
  var markdownParser = unified().use(remarkParse) // 解析 Markdown
@@ -355,6 +370,7 @@ var markdownParser = unified().use(remarkParse) // 解析 Markdown
355
370
  }) // GFM 插件,禁用单波浪线删除线
356
371
  .use(fixStrongWithSpecialChars) // 修复包含特殊字符的加粗文本
357
372
  .use(convertParagraphToImage) // 将以 ! 开头的段落转换为图片,将 | 开头的段落转换为表格
373
+ .use(protectJinjaDollarInText) // 保护 Jinja 块内 $,避免被 remark-math 误解析
358
374
  .use(remarkMath, {
359
375
  singleDollarTextMath: true
360
376
  });
@@ -134,6 +134,45 @@ var createRange = function createRange(path, childIndex, offset, length) {
134
134
  }
135
135
  }, props);
136
136
  };
137
+ /** 创建可跨子节点的 range(用于 Jinja 等可能被 inline code 分割的匹配) */ var createRangeSpanningChildren = function createRangeSpanningChildren(path, children, globalStart, globalEnd) {
138
+ var props = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : {};
139
+ var offset = 0;
140
+ var anchorPath = null;
141
+ var anchorOffset = 0;
142
+ var focusPath = null;
143
+ var focusOffset = 0;
144
+ for(var i = 0; i < children.length; i++){
145
+ var _ref;
146
+ var _children_i;
147
+ var text = (_ref = (_children_i = children[i]) === null || _children_i === void 0 ? void 0 : _children_i.text) !== null && _ref !== void 0 ? _ref : '';
148
+ var len = text.length;
149
+ var childStart = offset;
150
+ var childEnd = offset + len;
151
+ if (globalStart < childEnd && anchorPath === null) {
152
+ anchorPath = path.concat(i);
153
+ anchorOffset = Math.max(0, globalStart - childStart);
154
+ }
155
+ if (globalEnd <= childEnd && focusPath === null) {
156
+ focusPath = path.concat(i);
157
+ focusOffset = Math.min(len, globalEnd - childStart);
158
+ break;
159
+ }
160
+ offset = childEnd;
161
+ }
162
+ if (anchorPath && focusPath) {
163
+ return _object_spread({
164
+ anchor: {
165
+ path: anchorPath,
166
+ offset: anchorOffset
167
+ },
168
+ focus: {
169
+ path: focusPath,
170
+ offset: focusOffset
171
+ }
172
+ }, props);
173
+ }
174
+ return null;
175
+ };
137
176
  // 处理文本节点的匹配逻辑
138
177
  var processTextMatches = function processTextMatches(text, path, childIndex) {
139
178
  var ranges = [];
@@ -176,16 +215,18 @@ var processLinkMatches = function processLinkMatches(text, path, childIndex) {
176
215
  }
177
216
  return ranges;
178
217
  };
179
- var processJinjaMatches = function processJinjaMatches(text, path, childIndex) {
218
+ /** 在整段文本上匹配 Jinja,支持被 inline code 分割的语法(如 {% if `x` %}) */ var processJinjaMatchesOnFullText = function processJinjaMatchesOnFullText(fullText, path, children) {
180
219
  var ranges = [];
181
220
  var collect = function collect(reg, prop) {
182
221
  reg.lastIndex = 0;
183
222
  var match;
184
- while((match = reg.exec(text)) !== null){
185
- var index = match.index;
186
- if (typeof index === 'number') {
187
- ranges.push(createRange(path, childIndex, index, match[0].length, _define_property({}, prop, true)));
188
- }
223
+ while((match = reg.exec(fullText)) !== null){
224
+ var start = match.index;
225
+ var matched = match[0];
226
+ if (typeof start !== 'number' || !matched) continue;
227
+ var end = start + matched.length;
228
+ var range = createRangeSpanningChildren(path, children, start, end, _define_property({}, prop, true));
229
+ if (range) ranges.push(range);
189
230
  }
190
231
  };
191
232
  collect(JINJA_VARIABLE_REG, 'jinjaVariable');
@@ -213,6 +254,7 @@ export function useHighlight(store, jinjaEnabled) {
213
254
  var allTextRanges = [];
214
255
  var children = node.children;
215
256
  var childrenLength = children.length;
257
+ var fullText = Node.string(node);
216
258
  for(var i = 0; i < childrenLength; i++){
217
259
  var child = children[i];
218
260
  // 处理 footnote 和 HTML
@@ -225,10 +267,11 @@ export function useHighlight(store, jinjaEnabled) {
225
267
  var _allTextRanges1;
226
268
  (_allTextRanges1 = allTextRanges).push.apply(_allTextRanges1, _to_consumable_array(processLinkMatches(child.text, path, i)));
227
269
  }
228
- if (jinjaEnabled && child.text && !EditorUtils.isDirtLeaf(child)) {
229
- var _allTextRanges2;
230
- (_allTextRanges2 = allTextRanges).push.apply(_allTextRanges2, _to_consumable_array(processJinjaMatches(child.text, path, i)));
231
- }
270
+ }
271
+ // Jinja 在整段文本上匹配,支持 {% if `x` %} 等被 inline code 分割的语法
272
+ if (jinjaEnabled && fullText) {
273
+ var _allTextRanges2;
274
+ (_allTextRanges2 = allTextRanges).push.apply(_allTextRanges2, _to_consumable_array(processJinjaMatchesOnFullText(fullText, path, children)));
232
275
  }
233
276
  // 统一缓存
234
277
  cacheTextNode.set(node, {
@@ -257,8 +257,8 @@ import { useEditorStore } from "../store";
257
257
  var codeMatch = str.match(/^```([\w+\-#]+)$/i);
258
258
  if (codeMatch) {} else {
259
259
  var strAfterKey = str + (e.key.length === 1 ? e.key : '');
260
- // 仅在实际输入一个字符且刚好补全 trigger 时打开面板,避免 Backspace 等导致误打开
261
- if (jinjaTemplatePanelEnabled && e.key.length === 1 && strAfterKey === jinjaTrigger && setOpenJinjaTemplate && setJinjaAnchorPath) {
260
+ // 在实际输入一个字符且补全 trigger 时打开面板,支持在文本后输入(如 "hello {}")
261
+ if (jinjaTemplatePanelEnabled && e.key.length === 1 && strAfterKey.endsWith(jinjaTrigger) && setOpenJinjaTemplate && setJinjaAnchorPath) {
262
262
  setJinjaAnchorPath(node3[1]);
263
263
  setTimeout(function() {
264
264
  return setOpenJinjaTemplate(true);
@@ -498,7 +498,7 @@ var genStyle = function genStyle(token) {
498
498
  marginTop: '0.5em',
499
499
  marginBottom: '0.5em'
500
500
  },
501
- '& &-inline-code': {
501
+ '& code&-inline-code': {
502
502
  display: 'inline',
503
503
  fontFamily: "'Roboto,Mono SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace",
504
504
  margin: '1px 3px',
@@ -170,7 +170,8 @@ import remarkParse from "remark-parse";
170
170
  import remarkRehype from "remark-rehype";
171
171
  import { unified } from "unified";
172
172
  import { visit } from "unist-util-visit";
173
- import { convertParagraphToImage, fixStrongWithSpecialChars } from "../parser/remarkParse";
173
+ import { JINJA_DOLLAR_PLACEHOLDER } from "../parser/constants";
174
+ import { convertParagraphToImage, fixStrongWithSpecialChars, protectJinjaDollarInText } from "../parser/remarkParse";
174
175
  // HTML 转义相关的正则表达式和工具
175
176
  var ESCAPE_TEST_NO_ENCODE = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
176
177
  var ESCAPE_TEST = /[&<>"']/;
@@ -305,6 +306,7 @@ export var DEFAULT_MARKDOWN_REMARK_PLUGINS = [
305
306
  ],
306
307
  fixStrongWithSpecialChars,
307
308
  convertParagraphToImage,
309
+ protectJinjaDollarInText,
308
310
  [
309
311
  remarkMath,
310
312
  INLINE_MATH_WITH_SINGLE_DOLLAR
@@ -414,7 +416,7 @@ var createMarkdownProcessor = function createMarkdownProcessor(plugins, config)
414
416
  htmlContent = _state.sent();
415
417
  return [
416
418
  2,
417
- String(htmlContent)
419
+ String(htmlContent).split(JINJA_DOLLAR_PLACEHOLDER).join('$')
418
420
  ];
419
421
  case 2:
420
422
  error = _state.sent();
@@ -463,7 +465,7 @@ var createMarkdownProcessor = function createMarkdownProcessor(plugins, config)
463
465
  */ export var markdownToHtmlSync = function markdownToHtmlSync(markdown, plugins, config) {
464
466
  try {
465
467
  var file = createMarkdownProcessor(plugins, config).processSync(markdown);
466
- return String(file);
468
+ return String(file).split(JINJA_DOLLAR_PLACEHOLDER).join('$');
467
469
  } catch (error) {
468
470
  console.error('Error converting markdown to HTML:', error);
469
471
  return '';
@@ -1,3 +1,11 @@
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_with_holes(arr) {
7
+ if (Array.isArray(arr)) return arr;
8
+ }
1
9
  function _define_property(obj, key, value) {
2
10
  if (key in obj) {
3
11
  Object.defineProperty(obj, key, {
@@ -11,6 +19,33 @@ function _define_property(obj, key, value) {
11
19
  }
12
20
  return obj;
13
21
  }
22
+ function _iterable_to_array_limit(arr, i) {
23
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
24
+ if (_i == null) return;
25
+ var _arr = [];
26
+ var _n = true;
27
+ var _d = false;
28
+ var _s, _e;
29
+ try {
30
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
31
+ _arr.push(_s.value);
32
+ if (i && _arr.length === i) break;
33
+ }
34
+ } catch (err) {
35
+ _d = true;
36
+ _e = err;
37
+ } finally{
38
+ try {
39
+ if (!_n && _i["return"] != null) _i["return"]();
40
+ } finally{
41
+ if (_d) throw _e;
42
+ }
43
+ }
44
+ return _arr;
45
+ }
46
+ function _non_iterable_rest() {
47
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
48
+ }
14
49
  function _object_spread(target) {
15
50
  for(var i = 1; i < arguments.length; i++){
16
51
  var source = arguments[i] != null ? arguments[i] : {};
@@ -26,7 +61,19 @@ function _object_spread(target) {
26
61
  }
27
62
  return target;
28
63
  }
29
- import React from "react";
64
+ function _sliced_to_array(arr, i) {
65
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
66
+ }
67
+ function _unsupported_iterable_to_array(o, minLen) {
68
+ if (!o) return;
69
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
70
+ var n = Object.prototype.toString.call(o).slice(8, -1);
71
+ if (n === "Object" && o.constructor) n = o.constructor.name;
72
+ if (n === "Map" || n === "Set") return Array.from(n);
73
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
74
+ }
75
+ import React, { useEffect, useState } from "react";
76
+ /** 图例每页显示条数,超过则显示分页 */ var LEGEND_PAGE_SIZE = 12;
30
77
  var Legend = function Legend(param) {
31
78
  var chartData = param.chartData, backgroundColors = param.backgroundColors, hiddenDataIndicesByChart = param.hiddenDataIndicesByChart, chartIndex = param.chartIndex, onLegendItemClick = param.onLegendItemClick, total = param.total, baseClassName = param.baseClassName, hashId = param.hashId, isMobile = param.isMobile;
32
79
  var hiddenDataIndices = React.useMemo(function() {
@@ -35,23 +82,53 @@ var Legend = function Legend(param) {
35
82
  hiddenDataIndicesByChart,
36
83
  chartIndex
37
84
  ]);
85
+ var totalPages = Math.max(1, Math.ceil(chartData.length / LEGEND_PAGE_SIZE));
86
+ var _useState = _sliced_to_array(useState(0), 2), currentPage = _useState[0], setCurrentPage = _useState[1];
87
+ useEffect(function() {
88
+ setCurrentPage(0);
89
+ }, [
90
+ chartData.length,
91
+ chartIndex
92
+ ]);
93
+ var startIndex = currentPage * LEGEND_PAGE_SIZE;
94
+ var displayedItems = chartData.slice(startIndex, startIndex + LEGEND_PAGE_SIZE);
95
+ var handlePrevPage = function handlePrevPage() {
96
+ setCurrentPage(function(p) {
97
+ return Math.max(0, p - 1);
98
+ });
99
+ };
100
+ var handleNextPage = function handleNextPage() {
101
+ setCurrentPage(function(p) {
102
+ return Math.min(totalPages - 1, p + 1);
103
+ });
104
+ };
38
105
  return /*#__PURE__*/ React.createElement("div", {
39
106
  className: [
40
107
  "".concat(baseClassName, "-legend"),
41
108
  hashId
42
109
  ].filter(Boolean).join(' '),
43
110
  style: _object_spread({
44
- marginLeft: isMobile ? 0 : 12,
45
- maxHeight: isMobile ? '120px' : 'none',
46
- overflowY: isMobile ? 'auto' : 'visible'
111
+ marginLeft: isMobile ? 0 : 12
47
112
  }, isMobile ? {
113
+ display: 'flex',
114
+ flexDirection: 'column',
115
+ maxHeight: '200px',
116
+ minHeight: 0,
48
117
  alignSelf: 'center'
49
118
  } : {})
50
- }, chartData.map(function(d, i) {
51
- var isHidden = hiddenDataIndices.has(i);
119
+ }, /*#__PURE__*/ React.createElement("div", {
120
+ style: _object_spread({}, isMobile ? {
121
+ flex: 1,
122
+ minHeight: 0,
123
+ overflowY: 'auto',
124
+ overflowX: 'hidden'
125
+ } : {})
126
+ }, displayedItems.map(function(d, i) {
127
+ var dataIndex = startIndex + i;
128
+ var isHidden = hiddenDataIndices.has(dataIndex);
52
129
  var _obj;
53
130
  return /*#__PURE__*/ React.createElement("div", {
54
- key: i,
131
+ key: dataIndex,
55
132
  className: [
56
133
  "".concat(baseClassName, "-legend-item"),
57
134
  hashId
@@ -64,12 +141,12 @@ var Legend = function Legend(param) {
64
141
  textDecoration: isHidden ? 'line-through' : 'none'
65
142
  },
66
143
  onClick: function onClick() {
67
- return onLegendItemClick(i);
144
+ return onLegendItemClick(dataIndex);
68
145
  },
69
146
  onKeyDown: function onKeyDown(e) {
70
147
  if (e.key === 'Enter' || e.key === ' ') {
71
148
  e.preventDefault();
72
- onLegendItemClick(i);
149
+ onLegendItemClick(dataIndex);
73
150
  }
74
151
  },
75
152
  tabIndex: 0,
@@ -80,7 +157,7 @@ var Legend = function Legend(param) {
80
157
  "".concat(baseClassName, "-legend-color"),
81
158
  hashId
82
159
  ].filter(Boolean).join(' '),
83
- style: (_obj = {}, _define_property(_obj, '--donut-legend-color', backgroundColors[i] || '#ccc'), _define_property(_obj, "width", isMobile ? 10 : 12), _define_property(_obj, "height", isMobile ? 10 : 12), _define_property(_obj, "borderRadius", 4), _define_property(_obj, "marginRight", isMobile ? 4 : 6), _obj)
160
+ style: (_obj = {}, _define_property(_obj, '--donut-legend-color', backgroundColors[dataIndex] || '#ccc'), _define_property(_obj, "width", isMobile ? 10 : 12), _define_property(_obj, "height", isMobile ? 10 : 12), _define_property(_obj, "borderRadius", 4), _define_property(_obj, "marginRight", isMobile ? 4 : 6), _obj)
84
161
  }), /*#__PURE__*/ React.createElement("span", {
85
162
  className: [
86
163
  "".concat(baseClassName, "-legend-label"),
@@ -118,6 +195,47 @@ var Legend = function Legend(param) {
118
195
  var v = typeof d.value === 'number' ? d.value : Number(d.value);
119
196
  return total > 0 && Number.isFinite(v) ? (v / total * 100).toFixed(0) : '0';
120
197
  }(), "%")));
121
- }));
198
+ })), totalPages > 1 && /*#__PURE__*/ React.createElement("div", {
199
+ className: [
200
+ "".concat(baseClassName, "-legend-pagination"),
201
+ hashId
202
+ ].filter(Boolean).join(' '),
203
+ style: isMobile ? {
204
+ flexShrink: 0
205
+ } : undefined
206
+ }, /*#__PURE__*/ React.createElement("button", {
207
+ type: "button",
208
+ "aria-label": "上一页",
209
+ disabled: currentPage <= 0,
210
+ onClick: handlePrevPage,
211
+ style: {
212
+ padding: '2px 6px',
213
+ fontSize: 12,
214
+ cursor: currentPage <= 0 ? 'not-allowed' : 'pointer',
215
+ opacity: currentPage <= 0 ? 0.5 : 1,
216
+ border: '1px solid rgba(0,0,0,0.15)',
217
+ borderRadius: 4,
218
+ background: '#fff'
219
+ }
220
+ }, "<"), /*#__PURE__*/ React.createElement("span", {
221
+ style: {
222
+ fontSize: 12,
223
+ color: '#767E8B'
224
+ }
225
+ }, currentPage + 1, "/", totalPages), /*#__PURE__*/ React.createElement("button", {
226
+ type: "button",
227
+ "aria-label": "下一页",
228
+ disabled: currentPage >= totalPages - 1,
229
+ onClick: handleNextPage,
230
+ style: {
231
+ padding: '2px 6px',
232
+ fontSize: 12,
233
+ cursor: currentPage >= totalPages - 1 ? 'not-allowed' : 'pointer',
234
+ opacity: currentPage >= totalPages - 1 ? 0.5 : 1,
235
+ border: '1px solid rgba(0,0,0,0.15)',
236
+ borderRadius: 4,
237
+ background: '#fff'
238
+ }
239
+ }, ">")));
122
240
  };
123
241
  export default Legend;
@@ -146,6 +146,7 @@ function _unsupported_iterable_to_array(o, minLen) {
146
146
  }
147
147
  import { ConfigProvider } from "antd";
148
148
  import { ArcElement, Chart as ChartJS, Legend, Tooltip } from "chart.js";
149
+ import ChartDataLabels from "chartjs-plugin-datalabels";
149
150
  import classNames from "clsx";
150
151
  import React, { useContext, useMemo, useRef, useState } from "react";
151
152
  import { Doughnut } from "react-chartjs-2";
@@ -156,7 +157,7 @@ import { resolveCssVariable } from "../utils";
156
157
  import { SINGLE_MODE_DESKTOP_CUTOUT, SINGLE_MODE_MOBILE_CUTOUT } from "./constants";
157
158
  import { useAutoCategory, useFilterLabels, useMobile, useResponsiveDimensions } from "./hooks";
158
159
  import LegendView from "./Legend";
159
- import { createBackgroundArcPlugin, createCenterTextPlugin } from "./plugins";
160
+ import { createBackgroundArcPlugin, createCenterTextPlugin, createDataLabelsLeaderLinePlugin } from "./plugins";
160
161
  import { useStyle } from "./style";
161
162
  /**
162
163
  * @fileoverview 环形图组件文件
@@ -225,7 +226,7 @@ import { useStyle } from "./style";
225
226
  if (!isWindowDefined()) {
226
227
  return undefined;
227
228
  }
228
- ChartJS.register(ArcElement, Tooltip, Legend);
229
+ ChartJS.register(ArcElement, Tooltip, Legend, ChartDataLabels);
229
230
  donutChartComponentsRegistered = true;
230
231
  return undefined;
231
232
  }, []);
@@ -546,6 +547,9 @@ import { useStyle } from "./style";
546
547
  legend: {
547
548
  display: false
548
549
  },
550
+ /** 默认不展示扇区上的数值与占比,仅通过 configs[].showDataLabels === true 开启 */ datalabels: {
551
+ display: false
552
+ },
549
553
  tooltip: {
550
554
  enabled: cfg.showTooltip !== false,
551
555
  filter: function filter(tooltipItem) {
@@ -568,7 +572,10 @@ import { useStyle } from "./style";
568
572
  var label = param.label, raw = param.raw;
569
573
  var val = typeof raw === 'number' ? raw : Number(raw);
570
574
  var pct = total > 0 && Number.isFinite(val) ? (val / total * 100).toFixed(0) : '0';
571
- return "".concat(label, ": ").concat(Number.isFinite(val) ? val : raw, " (").concat(pct, "%)");
575
+ if (cfg.showDataLabels) {
576
+ return "".concat(label, ": ").concat(Number.isFinite(val) ? val : raw, " (").concat(pct, "%)");
577
+ }
578
+ return "".concat(label, " (").concat(pct, "%)");
572
579
  }
573
580
  }
574
581
  }
@@ -584,6 +591,43 @@ import { useStyle } from "./style";
584
591
  padding: isMobile ? 4 : 6
585
592
  }
586
593
  };
594
+ var dataLabelOffset = isMobile ? 22 : 36;
595
+ var optionsWithDataLabels = _object_spread_props(_object_spread({}, options, cfg.showDataLabels === true && !isSingleValueMode && {
596
+ layout: _object_spread_props(_object_spread({}, options.layout), {
597
+ padding: isMobile ? 36 : 52
598
+ })
599
+ }), {
600
+ plugins: _object_spread({}, options.plugins, cfg.showDataLabels === true && !isSingleValueMode && {
601
+ datalabels: {
602
+ display: function display(context) {
603
+ var _context_dataset_data, _context_dataset;
604
+ var value = (_context_dataset = context.dataset) === null || _context_dataset === void 0 ? void 0 : (_context_dataset_data = _context_dataset.data) === null || _context_dataset_data === void 0 ? void 0 : _context_dataset_data[context.dataIndex];
605
+ var numVal = typeof value === 'number' ? value : Number(value);
606
+ if (!Number.isFinite(numVal) || total <= 0) return false;
607
+ var pct = numVal / total * 100;
608
+ if (pct < 2) return false;
609
+ return true;
610
+ },
611
+ formatter: function formatter(value, context) {
612
+ var _context_chart_data_labels;
613
+ var pct = total > 0 ? (value / total * 100).toFixed(2) : '0';
614
+ var label = (_context_chart_data_labels = context.chart.data.labels) === null || _context_chart_data_labels === void 0 ? void 0 : _context_chart_data_labels[context.dataIndex];
615
+ var labelStr = label !== undefined && label !== null ? String(label) : '';
616
+ return labelStr ? "".concat(labelStr, ": ").concat(pct, "%") : "".concat(value, " (").concat(pct, "%)");
617
+ },
618
+ color: isDarkTheme ? '#fff' : '#343A45',
619
+ font: {
620
+ size: isMobile ? 10 : 11,
621
+ weight: 'normal'
622
+ },
623
+ anchor: 'end',
624
+ align: 'end',
625
+ offset: dataLabelOffset,
626
+ clamp: false,
627
+ clip: false
628
+ }
629
+ })
630
+ });
587
631
  var _obj, _obj1;
588
632
  return /*#__PURE__*/ React.createElement(ChartContainer, {
589
633
  key: idx,
@@ -622,10 +666,16 @@ import { useStyle } from "./style";
622
666
  chartRefs.current[idx] = instance;
623
667
  },
624
668
  data: chartJsData,
625
- options: options
669
+ options: optionsWithDataLabels,
670
+ plugins: cfg.showDataLabels === true && !isSingleValueMode ? [
671
+ createDataLabelsLeaderLinePlugin(dataLabelOffset),
672
+ ChartDataLabels
673
+ ] : []
626
674
  })), cfg.showLegend && /*#__PURE__*/ React.createElement(LegendView, {
627
675
  chartData: chartData,
628
- backgroundColors: backgroundColors,
676
+ backgroundColors: chartData.map(function(_, i) {
677
+ return backgroundColors[i % backgroundColors.length];
678
+ }),
629
679
  hiddenDataIndicesByChart: hiddenDataIndicesByChart,
630
680
  chartIndex: idx,
631
681
  onLegendItemClick: function onLegendItemClick(dataIndex) {
@@ -1,3 +1,8 @@
1
1
  import { Plugin } from 'chart.js';
2
+ /**
3
+ * 数据标签指示线插件:仅在会展示数据标签的扇区上绘制线(与 datalabels display 逻辑一致,避免无线无文案)。
4
+ * 传入的 lineLength 应与 options.plugins.datalabels.offset 一致。
5
+ */
6
+ export declare const createDataLabelsLeaderLinePlugin: (lineLength?: number) => Plugin<'doughnut'>;
2
7
  export declare const createCenterTextPlugin: (value: number, label: string, isMobile?: boolean) => Plugin<'doughnut'>;
3
8
  export declare const createBackgroundArcPlugin: (bgColor?: string, padding?: number) => Plugin<'doughnut'>;
@@ -1,3 +1,51 @@
1
+ /** 指示线默认长度(像素),需与 datalabels 的 offset 一致,使线头与文案衔接 */ var DEFAULT_LEADER_LINE_PX = 4;
2
+ /** 曲线控制点偏移系数,使指示线呈柔和弧线 */ var CURVE_OFFSET = 12;
3
+ /** 与 datalabels 的 display 阈值一致:占比不超过此不展示标签,也不画指示线 */ var MIN_PCT_FOR_LABEL = 2;
4
+ /**
5
+ * 数据标签指示线插件:仅在会展示数据标签的扇区上绘制线(与 datalabels display 逻辑一致,避免无线无文案)。
6
+ * 传入的 lineLength 应与 options.plugins.datalabels.offset 一致。
7
+ */ export var createDataLabelsLeaderLinePlugin = function createDataLabelsLeaderLinePlugin() {
8
+ var lineLength = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : DEFAULT_LEADER_LINE_PX;
9
+ return {
10
+ id: 'datalabelsLeaderLine',
11
+ afterDatasetDraw: function afterDatasetDraw(chart, args) {
12
+ var _chart_data_datasets, _meta_data;
13
+ var ctx = chart.ctx;
14
+ var meta = chart.getDatasetMeta(args.index);
15
+ var dataset = (_chart_data_datasets = chart.data.datasets) === null || _chart_data_datasets === void 0 ? void 0 : _chart_data_datasets[args.index];
16
+ var values = dataset === null || dataset === void 0 ? void 0 : dataset.data;
17
+ if (!(meta === null || meta === void 0 ? void 0 : (_meta_data = meta.data) === null || _meta_data === void 0 ? void 0 : _meta_data.length) || !(values === null || values === void 0 ? void 0 : values.length)) return;
18
+ var total = values.reduce(function(s, v) {
19
+ return s + (Number.isFinite(Number(v)) ? Number(v) : 0);
20
+ }, 0);
21
+ if (total <= 0) return;
22
+ ctx.save();
23
+ ctx.strokeStyle = 'rgba(0, 25, 61, 0.16)';
24
+ ctx.lineWidth = 1;
25
+ meta.data.forEach(function(element, i) {
26
+ var val = Number(values[i]);
27
+ if (!Number.isFinite(val) || val / total * 100 < MIN_PCT_FOR_LABEL) return;
28
+ var arc = element;
29
+ var angle = (arc.startAngle + arc.endAngle) / 2;
30
+ var cos = Math.cos(angle);
31
+ var sin = Math.sin(angle);
32
+ var x1 = arc.x + arc.outerRadius * cos;
33
+ var y1 = arc.y + arc.outerRadius * sin;
34
+ var x2 = arc.x + (arc.outerRadius + lineLength) * cos;
35
+ var y2 = arc.y + (arc.outerRadius + lineLength) * sin;
36
+ var midX = (x1 + x2) / 2;
37
+ var midY = (y1 + y2) / 2;
38
+ var ctrlX = midX + CURVE_OFFSET * -sin;
39
+ var ctrlY = midY + CURVE_OFFSET * cos;
40
+ ctx.beginPath();
41
+ ctx.moveTo(x1, y1);
42
+ ctx.quadraticCurveTo(ctrlX, ctrlY, x2, y2);
43
+ ctx.stroke();
44
+ });
45
+ ctx.restore();
46
+ }
47
+ };
48
+ };
1
49
  export var createCenterTextPlugin = function createCenterTextPlugin(value, label) {
2
50
  var isMobile = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false;
3
51
  return {
@@ -77,20 +77,24 @@ var genStyle = function genStyle(token) {
77
77
  gap: 8,
78
78
  marginBottom: 6
79
79
  }),
80
- '&-chart-wrapper': _define_property({}, "@media (max-width: 768px)", {
80
+ '&-chart-wrapper': _define_property({
81
+ overflow: 'visible'
82
+ }, "@media (max-width: 768px)", {
81
83
  marginBottom: 12
82
84
  }),
83
85
  '&-row': _define_property({
84
86
  display: 'flex',
85
87
  alignItems: 'center',
86
- flexDirection: 'row'
88
+ flexDirection: 'row',
89
+ overflow: 'visible'
87
90
  }, "@media (max-width: 768px)", {
88
91
  flexDirection: 'column',
89
92
  alignItems: 'stretch'
90
93
  }),
91
94
  '&-chart': _define_property({
92
95
  width: 'var(--donut-chart-width, 200px)',
93
- height: 'var(--donut-chart-height, 200px)'
96
+ height: 'var(--donut-chart-height, 200px)',
97
+ overflow: 'visible'
94
98
  }, "@media (max-width: 768px)", {
95
99
  alignSelf: 'center',
96
100
  marginBottom: 8
@@ -105,9 +109,7 @@ var genStyle = function genStyle(token) {
105
109
  marginLeft: 12
106
110
  }, "@media (max-width: 768px)", {
107
111
  marginLeft: 0,
108
- marginTop: 8,
109
- maxHeight: '120px',
110
- overflowY: 'auto'
112
+ marginTop: 8
111
113
  }),
112
114
  '&-legend-item': _define_property({
113
115
  display: 'flex',
@@ -168,6 +170,15 @@ var genStyle = function genStyle(token) {
168
170
  fontSize: 10,
169
171
  marginTop: 1
170
172
  }),
173
+ '&-legend-pagination': {
174
+ display: 'flex',
175
+ alignItems: 'center',
176
+ justifyContent: 'center',
177
+ gap: 8,
178
+ marginTop: 8,
179
+ paddingTop: 8,
180
+ borderTop: '1px solid rgba(0,0,0,0.06)'
181
+ },
171
182
  '&-statistic-container': {
172
183
  display: 'flex',
173
184
  gap: '16px',
@@ -19,6 +19,8 @@ export interface DonutChartConfig {
19
19
  borderColor?: string;
20
20
  /** 图表样式:'donut' 为环形图(默认),'pie' 为饼图 */
21
21
  chartStyle?: 'donut' | 'pie';
22
+ /** 是否展示数据标签与指示线:开启后扇区外显示数值/占比及连接线,tooltip 中也会展示原始数值,默认 false */
23
+ showDataLabels?: boolean;
22
24
  }
23
25
  export interface DonutChartProps extends ChartContainerProps {
24
26
  data: DonutChartData[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ant-design/agentic-ui",
3
- "version": "2.29.37",
3
+ "version": "2.29.38",
4
4
  "description": "面向智能体的 UI 组件库,提供多步推理可视化、工具调用展示、任务执行协同等 Agentic UI 能力",
5
5
  "repository": "git@github.com:ant-design/agentic-ui.git",
6
6
  "license": "MIT",
@@ -24,6 +24,7 @@
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": "father doctor && pnpm run test && pnpm run build",
27
28
  "prettier": "prettier --write \"{src,docs,test}/**/*.{js,jsx,ts,tsx,css,less,json,md}\"",
28
29
  "preview": "pnpm dumi preview",
29
30
  "report:demo": "node scripts/generateDemoReport.js",
@@ -84,6 +85,7 @@
84
85
  "is-hotkey": "^0.2.0",
85
86
  "json5": "^2.2.3",
86
87
  "katex": "^0.16.28",
88
+ "lodash": "^4.17.23",
87
89
  "lodash-es": "^4.17.23",
88
90
  "lottie-react": "^2.4.1",
89
91
  "mermaid": "^11.12.2",