@ant-design/agentic-ui 2.24.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.
- package/dist/Bubble/List/PureBubbleList.js +16 -2
- package/dist/Bubble/List/index.js +17 -2
- package/dist/MarkdownEditor/editor/elements/index.js +50 -1
- package/dist/MarkdownEditor/editor/parser/parseCache.d.ts +53 -0
- package/dist/MarkdownEditor/editor/parser/parseCache.js +355 -0
- package/dist/MarkdownEditor/editor/parser/parserMarkdownToSlateNode.js +6 -36
- package/dist/MarkdownEditor/editor/tools/ToolBar/ReadonlyBaseBar.js +2 -1
- package/dist/MarkdownEditor/types.d.ts +5 -0
- package/dist/MarkdownInputField/MarkdownInputField.js +5 -0
- package/dist/Plugins/chart/hooks/index.d.ts +1 -1
- package/dist/Plugins/chart/utils.d.ts +1 -1
- package/dist/Plugins/chart/utils.js +1 -1
- package/package.json +1 -1
|
@@ -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:
|
|
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:
|
|
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,
|
|
@@ -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
|
+
}
|
|
@@ -59,30 +59,6 @@ function _object_spread(target) {
|
|
|
59
59
|
}
|
|
60
60
|
return target;
|
|
61
61
|
}
|
|
62
|
-
function ownKeys(object, enumerableOnly) {
|
|
63
|
-
var keys = Object.keys(object);
|
|
64
|
-
if (Object.getOwnPropertySymbols) {
|
|
65
|
-
var symbols = Object.getOwnPropertySymbols(object);
|
|
66
|
-
if (enumerableOnly) {
|
|
67
|
-
symbols = symbols.filter(function(sym) {
|
|
68
|
-
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
keys.push.apply(keys, symbols);
|
|
72
|
-
}
|
|
73
|
-
return keys;
|
|
74
|
-
}
|
|
75
|
-
function _object_spread_props(target, source) {
|
|
76
|
-
source = source != null ? source : {};
|
|
77
|
-
if (Object.getOwnPropertyDescriptors) {
|
|
78
|
-
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
|
|
79
|
-
} else {
|
|
80
|
-
ownKeys(Object(source)).forEach(function(key) {
|
|
81
|
-
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
return target;
|
|
85
|
-
}
|
|
86
62
|
function _to_consumable_array(arr) {
|
|
87
63
|
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
88
64
|
}
|
|
@@ -213,18 +189,12 @@ import mdastParser from "./remarkParse";
|
|
|
213
189
|
// 如果 HTML 注释不是代码块元数据注释,但包含 JSON 对象属性(如对齐注释),
|
|
214
190
|
// 应该跳过注释本身,但将属性应用到下一个元素
|
|
215
191
|
if (isHtmlComment && !isOtherPropsComment && htmlCommentProps && Object.keys(htmlCommentProps).length > 0) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return "continue";
|
|
223
|
-
} else {
|
|
224
|
-
config = _object_spread_props(_object_spread({}, config), {
|
|
225
|
-
config: _to_consumable_array((config === null || config === void 0 ? void 0 : config.config) || []).concat(_to_consumable_array(htmlCommentProps))
|
|
226
|
-
});
|
|
227
|
-
}
|
|
192
|
+
// 将对齐注释等非代码块元数据注释的属性存储到 contextProps 中,供下一个元素使用
|
|
193
|
+
contextProps = _object_spread({}, contextProps, htmlCommentProps);
|
|
194
|
+
// 同时将属性作为 config 传递,以便 applyContextPropsAndConfig 设置 otherProps
|
|
195
|
+
config = _object_spread({}, config, htmlCommentProps);
|
|
196
|
+
// 跳过 HTML 注释本身,避免生成独立的 HTML 代码节点
|
|
197
|
+
return "continue";
|
|
228
198
|
}
|
|
229
199
|
// 如果当前元素应该使用 contextProps 中的属性作为 config(用于设置 otherProps)
|
|
230
200
|
// 这主要针对对齐注释等场景,需要同时设置 contextProps 和 otherProps
|
|
@@ -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
|
}
|
|
@@ -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,
|
|
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
|
|
465
|
+
export { registerBarChartComponents, registerChartComponents, registerLineChartComponents } from "./utils/registerChart";
|