@alifd/chat 0.3.37-beta.8 → 0.3.37

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.
@@ -211,14 +211,19 @@
211
211
  }
212
212
 
213
213
  li>p {
214
- margin: 0.4rem 0;
214
+ margin: 0.3rem 0;
215
215
  }
216
216
 
217
217
  p {
218
- margin: 0.4rem 0;
218
+ margin: 0.3rem 0;
219
219
  line-height: 1.8;
220
220
  }
221
221
 
222
+ span {
223
+ line-height: 1.8;
224
+ margin: 0.3rem 0;
225
+ }
226
+
222
227
  blockquote {
223
228
  opacity: 1;
224
229
  border: none;
package/es/index.js CHANGED
@@ -32,4 +32,4 @@ export { default as CheckboxGroup } from './checkbox-group';
32
32
  export { default as Select } from './select';
33
33
  export { default as Flip } from './flip';
34
34
  export { default as ToolStatus } from './tool-status';
35
- export const version = '0.3.37-beta.8';
35
+ export const version = '0.3.37';
@@ -20,7 +20,7 @@ const DEFAULT_WHITELIST_TAGS = ['img', 'code', 'pre', 'link-reference'];
20
20
  * @when 需要解析和渲染 Markdown 文本内容时使用。 - Use when you need to parse and render Markdown text content.
21
21
  */
22
22
  const Markdown = forwardRef((_a, ref) => {
23
- var { className, onReady, content } = _a, others = __rest(_a, ["className", "onReady", "content"]);
23
+ var { className, onReady, content, needCodeHeader = true } = _a, others = __rest(_a, ["className", "onReady", "content", "needCodeHeader"]);
24
24
  useEffect(() => {
25
25
  // 初始化表情库
26
26
  init();
@@ -35,25 +35,46 @@ const Markdown = forwardRef((_a, ref) => {
35
35
  if (!text)
36
36
  return [];
37
37
  const whitelist = [...DEFAULT_WHITELIST_TAGS, ...(customWhitelist || [])];
38
- if (whitelist.length === 0) {
39
- return text.split('');
40
- }
41
- // 创建匹配白名单标签的正则表达式
42
- const tagPattern = new RegExp(`(<(${whitelist.join('|')}).*?>.*?<\\/\\2>)`, 'gs');
38
+ // 综合正则:匹配 markdown 链接或白名单标签
39
+ const markdownLinkPattern = /\[[^\]]*\]\([^)]*\)/g;
40
+ const tagPattern = whitelist.length > 0 ? new RegExp(`<(${whitelist.join('|')}).*?>.*?<\\/\\1>`, 'gs') : null;
43
41
  const parts = [];
44
42
  let lastIndex = 0;
43
+ // 找到所有匹配项的位置
44
+ const allMatches = [];
45
+ // 添加 markdown 链接匹配
45
46
  let match;
46
- // 分割文本,将白名单标签作为整体,其他文本按字符分割
47
- while ((match = tagPattern.exec(text)) !== null) {
48
- if (match.index > lastIndex) {
49
- // 添加标签前的普通文本,按字符分割
50
- parts.push(...text.substring(lastIndex, match.index).split(''));
47
+ while ((match = markdownLinkPattern.exec(text)) !== null) {
48
+ allMatches.push({
49
+ start: match.index,
50
+ end: match.index + match[0].length,
51
+ content: match[0]
52
+ });
53
+ }
54
+ // 添加 HTML 标签匹配
55
+ if (tagPattern) {
56
+ tagPattern.lastIndex = 0;
57
+ while ((match = tagPattern.exec(text)) !== null) {
58
+ allMatches.push({
59
+ start: match.index,
60
+ end: match.index + match[0].length,
61
+ content: match[0]
62
+ });
51
63
  }
52
- // 添加整个标签内容作为一项
53
- parts.push(match[1]);
54
- lastIndex = match.index + match[1].length;
55
64
  }
56
- // 添加剩余的普通文本,按字符分割
65
+ // 按位置排序
66
+ allMatches.sort((a, b) => a.start - b.start);
67
+ // 分割文本
68
+ for (const item of allMatches) {
69
+ // 添加匹配项之前的普通文本(逐字符)
70
+ if (item.start > lastIndex) {
71
+ parts.push(...text.substring(lastIndex, item.start).split(''));
72
+ }
73
+ // 添加整个匹配项
74
+ parts.push(item.content);
75
+ lastIndex = item.end;
76
+ }
77
+ // 添加剩余文本(逐字符)
57
78
  if (lastIndex < text.length) {
58
79
  parts.push(...text.substring(lastIndex).split(''));
59
80
  }
@@ -102,25 +123,35 @@ const Markdown = forwardRef((_a, ref) => {
102
123
  return;
103
124
  }
104
125
  // 固定吐字速度为50ms一次
105
- const timePerItem = 100;
106
- // 动态调整一次吐字的字数,确保1s内吐完,最少吐2个列表项
126
+ const timePerItem = 50;
127
+ // 动态调整一次吐字的字数,确保0.5s内吐完,最少吐2个列表项
107
128
  const itemsPerTime = Math.max(Math.floor(contentQueue.length / 10), 2);
108
129
  const processNextItem = () => {
109
- const currentQueue = contentQueue;
110
- if (currentQueue.length === 0) {
111
- return;
112
- }
113
- // 获取并移除队列中的第charsPerTime个列表项
114
- const nextItems = currentQueue.slice(0, itemsPerTime);
115
- const newQueue = currentQueue.slice(itemsPerTime);
116
- // 更新显示内容
117
- setDisplayedContent(prev => prev + nextItems.join(''));
118
- // 更新队列
119
- setContentQueue(newQueue);
130
+ setContentQueue(currentQueue => {
131
+ if (currentQueue.length === 0) {
132
+ return currentQueue;
133
+ }
134
+ // 获取并移除队列中的第itemsPerTime个列表项
135
+ const nextItems = currentQueue.slice(0, itemsPerTime);
136
+ const newQueue = currentQueue.slice(itemsPerTime);
137
+ // 更新显示内容
138
+ setDisplayedContent(prev => prev + nextItems.join(''));
139
+ return newQueue;
140
+ });
120
141
  };
121
- // 开始处理第一个字符
122
- clearTypeTimer(); // 确保没有并发定时器
123
- typeTimeoutRef.current = setTimeout(processNextItem, timePerItem);
142
+ // 清除现有定时器
143
+ clearTypeTimer();
144
+ // 立即吐出第一次字符
145
+ processNextItem();
146
+ // 设置后续的定时器,继续按50ms间隔吐字
147
+ typeTimeoutRef.current = setTimeout(() => {
148
+ const continueTyping = () => {
149
+ processNextItem();
150
+ // 继续设置下一次定时器
151
+ typeTimeoutRef.current = setTimeout(continueTyping, timePerItem);
152
+ };
153
+ continueTyping();
154
+ }, timePerItem);
124
155
  return clearTypeTimer;
125
156
  }, [contentQueue.length, others === null || others === void 0 ? void 0 : others.typewriterEffect]);
126
157
  // 处理 HTML 标签
@@ -221,11 +252,15 @@ const Markdown = forwardRef((_a, ref) => {
221
252
  // 这里的 </ xx> 都被转成了&#x2F , 所以会font标签的元素 后面都会多一个 </ xx>
222
253
  transformedContent = transformedContent.replace(/&#x2F;/g, '/');
223
254
  // transformedContent = transformedContent.replace(/&#x2F;&#x2F;/g, '//');
255
+ transformedContent = transformedContent.replace(/&#39;/g, "'");
224
256
  // 适配//开头不支持的图片地址
225
257
  transformedContent = replaceImageLinks(transformedContent);
226
258
  return md.render(transformedContent);
227
259
  }, [transformedContent]);
228
260
  useEffect(() => {
261
+ if (!needCodeHeader) {
262
+ return;
263
+ }
229
264
  if (containerRef.current) {
230
265
  // 清除现有的 .code-block-header 元素
231
266
  const existingHeaders = containerRef.current.querySelectorAll('.code-block-header');
@@ -76,4 +76,9 @@ export interface MarkdownProps extends React.HTMLAttributes<HTMLElement> {
76
76
  * @en Whitelist tags that will be put into typewriter queue as a whole
77
77
  */
78
78
  whitelistTags?: string[];
79
+ /**
80
+ * 是不是需要头部的代码复制区域
81
+ * @en Tags that will not be put into typewriter queue
82
+ */
83
+ needCodeHeader?: boolean;
79
84
  }
@@ -13,7 +13,7 @@ const SUCCESS_ICON = 'icon-check'; //'success'
13
13
  const ERROR_ICON = 'icon-cross1'; //'error'
14
14
  const LOADING_ICON = 'loading';
15
15
  const ToolStatus = forwardRef((props, ref) => {
16
- const { useText = '使用', name, className, selected = false, status, onToolClick, authInfo, fullWidth } = props;
16
+ const { useText = '使用', name, className, selected = false, status, onToolClick, authInfo, fullWidth, param, groupName } = props;
17
17
  const handleClick = (event) => {
18
18
  event.stopPropagation();
19
19
  onToolClick === null || onToolClick === void 0 ? void 0 : onToolClick(props);
@@ -30,8 +30,11 @@ const ToolStatus = forwardRef((props, ref) => {
30
30
  event.stopPropagation();
31
31
  (_a = authInfo === null || authInfo === void 0 ? void 0 : authInfo.onAuthClick) === null || _a === void 0 ? void 0 : _a.call(authInfo, props);
32
32
  } }, authInfo === null || authInfo === void 0 ? void 0 : authInfo.content),
33
- React.createElement("div", { className: 'content-use-tips' }, useText),
34
- React.createElement("div", { className: 'content-name' }, name)));
33
+ React.createElement("div", { className: 'content-container' },
34
+ React.createElement("div", { className: 'content-use-tips' }, useText),
35
+ groupName && React.createElement("div", { className: 'content-group' }, groupName),
36
+ name && React.createElement("div", { className: 'content-name' }, name),
37
+ param && React.createElement("div", { className: 'content-detail' }, param))));
35
38
  });
36
39
  export * from './types';
37
40
  export default ConfigProvider.config(ToolStatus);
@@ -54,22 +54,31 @@
54
54
  line-height: $s-4;
55
55
  font-size: $font-size-caption;
56
56
  cursor: pointer;
57
- margin-right: $s-1;
57
+ min-width: 52px;
58
58
  }
59
59
 
60
- .content-use-tips {
61
- color: $color-text1-3;
62
- margin-right: $s-1;
63
- line-height: $s-4;
60
+ .content-container {
61
+ flex: 1;
62
+ display: flex;
63
+ overflow: hidden;
64
64
  }
65
65
 
66
- .content-name {
67
- color: $color-text1-4;
66
+ .content-use-tips, .content-group, .content-name, .content-detail {
67
+ color: $color-text1-3;
68
68
  line-height: $s-4;
69
+ margin-right: $s-1;
69
70
  overflow: hidden;
70
71
  white-space: nowrap;
71
72
  text-overflow: ellipsis;
72
- flex: 1;
73
73
  }
74
74
 
75
+
76
+ .content-group {
77
+ color: $color-text1-4;
78
+ }
79
+
80
+ .content-detail {
81
+ flex: 1;
82
+ color: $color-text1-4;
83
+ }
75
84
  }
@@ -33,10 +33,18 @@ export interface ToolStatusProps extends React.HTMLAttributes<HTMLElement>, Comm
33
33
  * 工具名称
34
34
  */
35
35
  name?: string;
36
+ /**
37
+ * 工具集名称
38
+ */
39
+ groupName?: string;
36
40
  /**
37
41
  * 工具详情链接
38
42
  */
39
43
  useText?: string;
44
+ /**
45
+ * 当前的具体使用说明
46
+ */
47
+ param?: string;
40
48
  /**
41
49
  * 是否100%的宽度
42
50
  */
@@ -211,14 +211,19 @@
211
211
  }
212
212
 
213
213
  li>p {
214
- margin: 0.4rem 0;
214
+ margin: 0.3rem 0;
215
215
  }
216
216
 
217
217
  p {
218
- margin: 0.4rem 0;
218
+ margin: 0.3rem 0;
219
219
  line-height: 1.8;
220
220
  }
221
221
 
222
+ span {
223
+ line-height: 1.8;
224
+ margin: 0.3rem 0;
225
+ }
226
+
222
227
  blockquote {
223
228
  opacity: 1;
224
229
  border: none;
package/lib/index.js CHANGED
@@ -70,4 +70,4 @@ var flip_1 = require("./flip");
70
70
  Object.defineProperty(exports, "Flip", { enumerable: true, get: function () { return tslib_1.__importDefault(flip_1).default; } });
71
71
  var tool_status_1 = require("./tool-status");
72
72
  Object.defineProperty(exports, "ToolStatus", { enumerable: true, get: function () { return tslib_1.__importDefault(tool_status_1).default; } });
73
- exports.version = '0.3.37-beta.8';
73
+ exports.version = '0.3.37';
@@ -22,7 +22,7 @@ const DEFAULT_WHITELIST_TAGS = ['img', 'code', 'pre', 'link-reference'];
22
22
  * @when 需要解析和渲染 Markdown 文本内容时使用。 - Use when you need to parse and render Markdown text content.
23
23
  */
24
24
  const Markdown = (0, react_1.forwardRef)((_a, ref) => {
25
- var { className, onReady, content } = _a, others = tslib_1.__rest(_a, ["className", "onReady", "content"]);
25
+ var { className, onReady, content, needCodeHeader = true } = _a, others = tslib_1.__rest(_a, ["className", "onReady", "content", "needCodeHeader"]);
26
26
  (0, react_1.useEffect)(() => {
27
27
  // 初始化表情库
28
28
  (0, dingtalk_im_emoji_1.init)();
@@ -37,25 +37,46 @@ const Markdown = (0, react_1.forwardRef)((_a, ref) => {
37
37
  if (!text)
38
38
  return [];
39
39
  const whitelist = [...DEFAULT_WHITELIST_TAGS, ...(customWhitelist || [])];
40
- if (whitelist.length === 0) {
41
- return text.split('');
42
- }
43
- // 创建匹配白名单标签的正则表达式
44
- const tagPattern = new RegExp(`(<(${whitelist.join('|')}).*?>.*?<\\/\\2>)`, 'gs');
40
+ // 综合正则:匹配 markdown 链接或白名单标签
41
+ const markdownLinkPattern = /\[[^\]]*\]\([^)]*\)/g;
42
+ const tagPattern = whitelist.length > 0 ? new RegExp(`<(${whitelist.join('|')}).*?>.*?<\\/\\1>`, 'gs') : null;
45
43
  const parts = [];
46
44
  let lastIndex = 0;
45
+ // 找到所有匹配项的位置
46
+ const allMatches = [];
47
+ // 添加 markdown 链接匹配
47
48
  let match;
48
- // 分割文本,将白名单标签作为整体,其他文本按字符分割
49
- while ((match = tagPattern.exec(text)) !== null) {
50
- if (match.index > lastIndex) {
51
- // 添加标签前的普通文本,按字符分割
52
- parts.push(...text.substring(lastIndex, match.index).split(''));
49
+ while ((match = markdownLinkPattern.exec(text)) !== null) {
50
+ allMatches.push({
51
+ start: match.index,
52
+ end: match.index + match[0].length,
53
+ content: match[0]
54
+ });
55
+ }
56
+ // 添加 HTML 标签匹配
57
+ if (tagPattern) {
58
+ tagPattern.lastIndex = 0;
59
+ while ((match = tagPattern.exec(text)) !== null) {
60
+ allMatches.push({
61
+ start: match.index,
62
+ end: match.index + match[0].length,
63
+ content: match[0]
64
+ });
53
65
  }
54
- // 添加整个标签内容作为一项
55
- parts.push(match[1]);
56
- lastIndex = match.index + match[1].length;
57
66
  }
58
- // 添加剩余的普通文本,按字符分割
67
+ // 按位置排序
68
+ allMatches.sort((a, b) => a.start - b.start);
69
+ // 分割文本
70
+ for (const item of allMatches) {
71
+ // 添加匹配项之前的普通文本(逐字符)
72
+ if (item.start > lastIndex) {
73
+ parts.push(...text.substring(lastIndex, item.start).split(''));
74
+ }
75
+ // 添加整个匹配项
76
+ parts.push(item.content);
77
+ lastIndex = item.end;
78
+ }
79
+ // 添加剩余文本(逐字符)
59
80
  if (lastIndex < text.length) {
60
81
  parts.push(...text.substring(lastIndex).split(''));
61
82
  }
@@ -104,25 +125,35 @@ const Markdown = (0, react_1.forwardRef)((_a, ref) => {
104
125
  return;
105
126
  }
106
127
  // 固定吐字速度为50ms一次
107
- const timePerItem = 100;
108
- // 动态调整一次吐字的字数,确保1s内吐完,最少吐2个列表项
128
+ const timePerItem = 50;
129
+ // 动态调整一次吐字的字数,确保0.5s内吐完,最少吐2个列表项
109
130
  const itemsPerTime = Math.max(Math.floor(contentQueue.length / 10), 2);
110
131
  const processNextItem = () => {
111
- const currentQueue = contentQueue;
112
- if (currentQueue.length === 0) {
113
- return;
114
- }
115
- // 获取并移除队列中的第charsPerTime个列表项
116
- const nextItems = currentQueue.slice(0, itemsPerTime);
117
- const newQueue = currentQueue.slice(itemsPerTime);
118
- // 更新显示内容
119
- setDisplayedContent(prev => prev + nextItems.join(''));
120
- // 更新队列
121
- setContentQueue(newQueue);
132
+ setContentQueue(currentQueue => {
133
+ if (currentQueue.length === 0) {
134
+ return currentQueue;
135
+ }
136
+ // 获取并移除队列中的第itemsPerTime个列表项
137
+ const nextItems = currentQueue.slice(0, itemsPerTime);
138
+ const newQueue = currentQueue.slice(itemsPerTime);
139
+ // 更新显示内容
140
+ setDisplayedContent(prev => prev + nextItems.join(''));
141
+ return newQueue;
142
+ });
122
143
  };
123
- // 开始处理第一个字符
124
- clearTypeTimer(); // 确保没有并发定时器
125
- typeTimeoutRef.current = setTimeout(processNextItem, timePerItem);
144
+ // 清除现有定时器
145
+ clearTypeTimer();
146
+ // 立即吐出第一次字符
147
+ processNextItem();
148
+ // 设置后续的定时器,继续按50ms间隔吐字
149
+ typeTimeoutRef.current = setTimeout(() => {
150
+ const continueTyping = () => {
151
+ processNextItem();
152
+ // 继续设置下一次定时器
153
+ typeTimeoutRef.current = setTimeout(continueTyping, timePerItem);
154
+ };
155
+ continueTyping();
156
+ }, timePerItem);
126
157
  return clearTypeTimer;
127
158
  }, [contentQueue.length, others === null || others === void 0 ? void 0 : others.typewriterEffect]);
128
159
  // 处理 HTML 标签
@@ -223,11 +254,15 @@ const Markdown = (0, react_1.forwardRef)((_a, ref) => {
223
254
  // 这里的 </ xx> 都被转成了&#x2F , 所以会font标签的元素 后面都会多一个 </ xx>
224
255
  transformedContent = transformedContent.replace(/&#x2F;/g, '/');
225
256
  // transformedContent = transformedContent.replace(/&#x2F;&#x2F;/g, '//');
257
+ transformedContent = transformedContent.replace(/&#39;/g, "'");
226
258
  // 适配//开头不支持的图片地址
227
259
  transformedContent = replaceImageLinks(transformedContent);
228
260
  return md.render(transformedContent);
229
261
  }, [transformedContent]);
230
262
  (0, react_1.useEffect)(() => {
263
+ if (!needCodeHeader) {
264
+ return;
265
+ }
231
266
  if (containerRef.current) {
232
267
  // 清除现有的 .code-block-header 元素
233
268
  const existingHeaders = containerRef.current.querySelectorAll('.code-block-header');
@@ -76,4 +76,9 @@ export interface MarkdownProps extends React.HTMLAttributes<HTMLElement> {
76
76
  * @en Whitelist tags that will be put into typewriter queue as a whole
77
77
  */
78
78
  whitelistTags?: string[];
79
+ /**
80
+ * 是不是需要头部的代码复制区域
81
+ * @en Tags that will not be put into typewriter queue
82
+ */
83
+ needCodeHeader?: boolean;
79
84
  }
@@ -16,7 +16,7 @@ const SUCCESS_ICON = 'icon-check'; //'success'
16
16
  const ERROR_ICON = 'icon-cross1'; //'error'
17
17
  const LOADING_ICON = 'loading';
18
18
  const ToolStatus = (0, react_1.forwardRef)((props, ref) => {
19
- const { useText = '使用', name, className, selected = false, status, onToolClick, authInfo, fullWidth } = props;
19
+ const { useText = '使用', name, className, selected = false, status, onToolClick, authInfo, fullWidth, param, groupName } = props;
20
20
  const handleClick = (event) => {
21
21
  event.stopPropagation();
22
22
  onToolClick === null || onToolClick === void 0 ? void 0 : onToolClick(props);
@@ -33,8 +33,11 @@ const ToolStatus = (0, react_1.forwardRef)((props, ref) => {
33
33
  event.stopPropagation();
34
34
  (_a = authInfo === null || authInfo === void 0 ? void 0 : authInfo.onAuthClick) === null || _a === void 0 ? void 0 : _a.call(authInfo, props);
35
35
  } }, authInfo === null || authInfo === void 0 ? void 0 : authInfo.content),
36
- react_1.default.createElement("div", { className: 'content-use-tips' }, useText),
37
- react_1.default.createElement("div", { className: 'content-name' }, name)));
36
+ react_1.default.createElement("div", { className: 'content-container' },
37
+ react_1.default.createElement("div", { className: 'content-use-tips' }, useText),
38
+ groupName && react_1.default.createElement("div", { className: 'content-group' }, groupName),
39
+ name && react_1.default.createElement("div", { className: 'content-name' }, name),
40
+ param && react_1.default.createElement("div", { className: 'content-detail' }, param))));
38
41
  });
39
42
  tslib_1.__exportStar(require("./types"), exports);
40
43
  exports.default = next_1.ConfigProvider.config(ToolStatus);
@@ -54,22 +54,31 @@
54
54
  line-height: $s-4;
55
55
  font-size: $font-size-caption;
56
56
  cursor: pointer;
57
- margin-right: $s-1;
57
+ min-width: 52px;
58
58
  }
59
59
 
60
- .content-use-tips {
61
- color: $color-text1-3;
62
- margin-right: $s-1;
63
- line-height: $s-4;
60
+ .content-container {
61
+ flex: 1;
62
+ display: flex;
63
+ overflow: hidden;
64
64
  }
65
65
 
66
- .content-name {
67
- color: $color-text1-4;
66
+ .content-use-tips, .content-group, .content-name, .content-detail {
67
+ color: $color-text1-3;
68
68
  line-height: $s-4;
69
+ margin-right: $s-1;
69
70
  overflow: hidden;
70
71
  white-space: nowrap;
71
72
  text-overflow: ellipsis;
72
- flex: 1;
73
73
  }
74
74
 
75
+
76
+ .content-group {
77
+ color: $color-text1-4;
78
+ }
79
+
80
+ .content-detail {
81
+ flex: 1;
82
+ color: $color-text1-4;
83
+ }
75
84
  }
@@ -33,10 +33,18 @@ export interface ToolStatusProps extends React.HTMLAttributes<HTMLElement>, Comm
33
33
  * 工具名称
34
34
  */
35
35
  name?: string;
36
+ /**
37
+ * 工具集名称
38
+ */
39
+ groupName?: string;
36
40
  /**
37
41
  * 工具详情链接
38
42
  */
39
43
  useText?: string;
44
+ /**
45
+ * 当前的具体使用说明
46
+ */
47
+ param?: string;
40
48
  /**
41
49
  * 是否100%的宽度
42
50
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alifd/chat",
3
- "version": "0.3.37-beta.8",
3
+ "version": "0.3.37",
4
4
  "description": "A configurable component library for chat built on React.",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.js",