@ai-group/chat-sdk 3.7.1-alpha2 → 3.7.1-alpha4

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.
Files changed (84) hide show
  1. package/dist/cjs/components/FileGallery/index.js +59 -40
  2. package/dist/cjs/components/FileGallery/index.js.map +2 -2
  3. package/dist/cjs/components/FileGallery/styles.d.ts +7 -0
  4. package/dist/cjs/components/FileGallery/styles.js +78 -0
  5. package/dist/cjs/components/FileGallery/styles.js.map +2 -2
  6. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/index.d.ts +7 -1
  7. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/index.js +82 -22
  8. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/index.js.map +2 -2
  9. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/styles.d.ts +3 -0
  10. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/styles.js +39 -0
  11. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/styles.js.map +2 -2
  12. package/dist/cjs/components/XAdkChatbot/index.js +35 -5
  13. package/dist/cjs/components/XAdkChatbot/index.js.map +2 -2
  14. package/dist/cjs/components/XAdkChatbot/styles.js +1 -1
  15. package/dist/cjs/components/XAdkChatbot/styles.js.map +1 -1
  16. package/dist/cjs/components/XAdkSender/InlineTagInput.d.ts +11 -5
  17. package/dist/cjs/components/XAdkSender/InlineTagInput.js +188 -113
  18. package/dist/cjs/components/XAdkSender/InlineTagInput.js.map +2 -2
  19. package/dist/cjs/components/XAdkSender/InputTags.d.ts +2 -2
  20. package/dist/cjs/components/XAdkSender/InputTags.js +8 -8
  21. package/dist/cjs/components/XAdkSender/InputTags.js.map +2 -2
  22. package/dist/cjs/components/XAdkSender/index.js +104 -64
  23. package/dist/cjs/components/XAdkSender/index.js.map +2 -2
  24. package/dist/cjs/components/XAdkSender/styles.d.ts +4 -0
  25. package/dist/cjs/components/XAdkSender/styles.js +34 -6
  26. package/dist/cjs/components/XAdkSender/styles.js.map +2 -2
  27. package/dist/cjs/index.d.ts +2 -1
  28. package/dist/cjs/index.js +7 -0
  29. package/dist/cjs/index.js.map +2 -2
  30. package/dist/cjs/types/FileGallery.d.ts +3 -0
  31. package/dist/cjs/types/FileGallery.js.map +1 -1
  32. package/dist/cjs/types/XAdkChatbot.d.ts +15 -0
  33. package/dist/cjs/types/XAdkChatbot.js.map +1 -1
  34. package/dist/cjs/types/XAdkSender.d.ts +74 -23
  35. package/dist/cjs/types/XAdkSender.js.map +1 -1
  36. package/dist/cjs/utils/index.d.ts +1 -0
  37. package/dist/cjs/utils/index.js +3 -1
  38. package/dist/cjs/utils/index.js.map +2 -2
  39. package/dist/cjs/utils/markdownResource.d.ts +7 -0
  40. package/dist/cjs/utils/markdownResource.js +73 -0
  41. package/dist/cjs/utils/markdownResource.js.map +7 -0
  42. package/dist/esm/components/FileGallery/index.js +22 -16
  43. package/dist/esm/components/FileGallery/index.js.map +1 -1
  44. package/dist/esm/components/FileGallery/styles.d.ts +7 -0
  45. package/dist/esm/components/FileGallery/styles.js +24 -17
  46. package/dist/esm/components/FileGallery/styles.js.map +1 -1
  47. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/index.d.ts +7 -1
  48. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/index.js +57 -7
  49. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/index.js.map +1 -1
  50. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/styles.d.ts +3 -0
  51. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/styles.js +5 -2
  52. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/styles.js.map +1 -1
  53. package/dist/esm/components/XAdkChatbot/index.js +20 -6
  54. package/dist/esm/components/XAdkChatbot/index.js.map +1 -1
  55. package/dist/esm/components/XAdkChatbot/styles.js +1 -1
  56. package/dist/esm/components/XAdkChatbot/styles.js.map +1 -1
  57. package/dist/esm/components/XAdkSender/InlineTagInput.d.ts +11 -5
  58. package/dist/esm/components/XAdkSender/InlineTagInput.js +185 -117
  59. package/dist/esm/components/XAdkSender/InlineTagInput.js.map +1 -1
  60. package/dist/esm/components/XAdkSender/InputTags.d.ts +2 -2
  61. package/dist/esm/components/XAdkSender/InputTags.js +9 -9
  62. package/dist/esm/components/XAdkSender/InputTags.js.map +1 -1
  63. package/dist/esm/components/XAdkSender/index.js +124 -77
  64. package/dist/esm/components/XAdkSender/index.js.map +1 -1
  65. package/dist/esm/components/XAdkSender/styles.d.ts +4 -0
  66. package/dist/esm/components/XAdkSender/styles.js +29 -25
  67. package/dist/esm/components/XAdkSender/styles.js.map +1 -1
  68. package/dist/esm/index.d.ts +2 -1
  69. package/dist/esm/index.js +2 -1
  70. package/dist/esm/index.js.map +1 -1
  71. package/dist/esm/types/FileGallery.d.ts +3 -0
  72. package/dist/esm/types/FileGallery.js.map +1 -1
  73. package/dist/esm/types/XAdkChatbot.d.ts +15 -0
  74. package/dist/esm/types/XAdkChatbot.js.map +1 -1
  75. package/dist/esm/types/XAdkSender.d.ts +74 -23
  76. package/dist/esm/types/XAdkSender.js.map +1 -1
  77. package/dist/esm/utils/index.d.ts +1 -0
  78. package/dist/esm/utils/index.js +1 -0
  79. package/dist/esm/utils/index.js.map +1 -1
  80. package/dist/esm/utils/markdownResource.d.ts +7 -0
  81. package/dist/esm/utils/markdownResource.js +33 -0
  82. package/dist/esm/utils/markdownResource.js.map +1 -0
  83. package/dist/umd/chat-sdk.min.js +1 -1
  84. package/package.json +1 -1
@@ -1,15 +1,17 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
2
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from "react";
2
+ import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useImperativeHandle, useRef } from "react";
3
3
  import { createRoot } from "react-dom/client";
4
4
  import { useStyles } from "./styles";
5
5
  import { Fragment as _Fragment } from "react/jsx-runtime";
6
6
  import { jsx as _jsx } from "react/jsx-runtime";
7
- var TAG_ATTR = "data-xadk-inline-tag-id";
8
- // contenteditable 无法把光标放进 contenteditable=false 的标签里,需用零宽字符承接“标签后方”的光标。
7
+ import { jsxs as _jsxs } from "react/jsx-runtime";
8
+ var MENTION_ATTR = "data-xadk-inline-mention-id";
9
+ // contenteditable 无法把光标放进 contenteditable=false 的 Mention 里,需用零宽字符承接“Mention 后方”的光标。
9
10
  var CARET_TEXT = "\u200B";
10
11
  var createTextNode = function createTextNode(text) {
11
12
  return document.createTextNode(text);
12
13
  };
14
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
13
15
  var normalizeText = function normalizeText(text) {
14
16
  return text.replace(/\u200B/g, "").replace(/\u00a0/g, " ");
15
17
  };
@@ -27,31 +29,40 @@ var toRawOffset = function toRawOffset(text, plainOffset) {
27
29
  };
28
30
  var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
29
31
  var value = _ref.value,
30
- tags = _ref.tags,
32
+ mentions = _ref.mentions,
33
+ defaultInputParts = _ref.defaultInputParts,
31
34
  placeholder = _ref.placeholder,
32
35
  disabled = _ref.disabled,
33
36
  onChange = _ref.onChange,
34
- onTagsChange = _ref.onTagsChange,
37
+ onMentionsChange = _ref.onMentionsChange,
35
38
  onKeyDown = _ref.onKeyDown,
36
- onKeyUp = _ref.onKeyUp,
37
- renderTag = _ref.renderTag;
39
+ _onKeyUp = _ref.onKeyUp,
40
+ renderMention = _ref.renderMention;
38
41
  var styles = useStyles();
39
42
  var editorRef = useRef(null);
40
- var latestTagsRef = useRef(tags);
43
+ var latestMentionsRef = useRef(mentions);
41
44
  var tagRootMapRef = useRef(new WeakMap());
42
45
  var composingRef = useRef(false);
43
46
  var lastInsertedCaretRef = useRef(null);
47
+ var savedRangeRef = useRef(null);
48
+ var defaultInputPartsAppliedRef = useRef(false);
49
+ var lastEmittedStateRef = useRef({
50
+ value: value,
51
+ mentionIds: mentions.map(function (mention) {
52
+ return mention.id;
53
+ }).join("|")
54
+ });
44
55
  useEffect(function () {
45
- latestTagsRef.current = tags;
46
- }, [tags]);
47
- var getOrderedTags = useCallback(function () {
56
+ latestMentionsRef.current = mentions;
57
+ }, [mentions]);
58
+ var getOrderedMentions = useCallback(function () {
48
59
  var editor = editorRef.current;
49
60
  if (!editor) return [];
50
- var tagMap = new Map(latestTagsRef.current.map(function (tag) {
51
- return [tag.id, tag];
61
+ var mentionMap = new Map(latestMentionsRef.current.map(function (mention) {
62
+ return [mention.id, mention];
52
63
  }));
53
- return Array.from(editor.querySelectorAll("[".concat(TAG_ATTR, "]"))).map(function (node) {
54
- return tagMap.get(node.dataset.xadkInlineTagId || "");
64
+ return Array.from(editor.querySelectorAll("[".concat(MENTION_ATTR, "]"))).map(function (node) {
65
+ return mentionMap.get(node.dataset.xadkInlineMentionId || "");
55
66
  }).filter(Boolean);
56
67
  }, []);
57
68
 
@@ -68,12 +79,12 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
68
79
  return normalizeText(text);
69
80
  }, []);
70
81
 
71
- // 提交时需要保留文本与 tag 的真实顺序;仅靠 value + tags 无法区分 tag 在句首、句中还是句尾。
82
+ // 提交时需要保留文本与 Mention 的真实顺序;仅靠 value + mentions 无法区分 Mention 在句首、句中还是句尾。
72
83
  var getParts = useCallback(function () {
73
84
  var editor = editorRef.current;
74
85
  if (!editor) return [];
75
- var tagMap = new Map(latestTagsRef.current.map(function (tag) {
76
- return [tag.id, tag];
86
+ var mentionMap = new Map(latestMentionsRef.current.map(function (mention) {
87
+ return [mention.id, mention];
77
88
  }));
78
89
  var parts = [];
79
90
  editor.childNodes.forEach(function (node) {
@@ -85,34 +96,42 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
85
96
  });
86
97
  return;
87
98
  }
88
- if (node instanceof HTMLElement && node.hasAttribute(TAG_ATTR)) {
89
- var tagId = node.dataset.xadkInlineTagId || "";
90
- var _tag = tagMap.get(tagId);
91
- if (_tag) parts.push({
92
- type: "tag",
93
- tag: _tag
99
+ if (node instanceof HTMLElement && node.hasAttribute(MENTION_ATTR)) {
100
+ var mentionId = node.dataset.xadkInlineMentionId || "";
101
+ var _mention = mentionMap.get(mentionId);
102
+ if (_mention) parts.push({
103
+ type: "mention",
104
+ mention: _mention
94
105
  });
95
106
  }
96
107
  });
97
108
  return parts;
98
109
  }, []);
99
110
  var emitChange = useCallback(function () {
100
- onChange(getPlainText());
101
- onTagsChange(getOrderedTags());
102
- }, [getOrderedTags, getPlainText, onChange, onTagsChange]);
111
+ var nextValue = getPlainText();
112
+ var nextMentions = getOrderedMentions();
113
+ lastEmittedStateRef.current = {
114
+ value: nextValue,
115
+ mentionIds: nextMentions.map(function (mention) {
116
+ return mention.id;
117
+ }).join("|")
118
+ };
119
+ onChange(nextValue);
120
+ onMentionsChange(nextMentions);
121
+ }, [getOrderedMentions, getPlainText, onChange, onMentionsChange]);
103
122
  var removeTextRange = useCallback(function (node, start, end) {
104
123
  var text = node.textContent || "";
105
124
  node.textContent = "".concat(text.slice(0, start)).concat(text.slice(end));
106
125
  if (!node.textContent) node.remove();
107
126
  }, []);
108
- var removeTagNode = useCallback(function (node) {
127
+ var removeMentionNode = useCallback(function (node) {
109
128
  var _tagRootMapRef$curren;
110
129
  var previous = node.previousSibling;
111
130
  var next = node.nextSibling;
112
131
  (_tagRootMapRef$curren = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren === void 0 || _tagRootMapRef$curren.unmount();
113
132
  node.remove();
114
133
 
115
- // 标签后会补一个不可见光标锚点;删除标签时同步清理,避免留下冗余节点。
134
+ // Mention 后会补一个不可见光标锚点;删除 Mention 时同步清理,避免留下冗余节点。
116
135
  if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE) {
117
136
  var _text$match;
118
137
  var text = next.textContent || "";
@@ -131,51 +150,68 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
131
150
  }
132
151
  }
133
152
  }, [removeTextRange]);
134
- var removeTagById = useCallback(function (id) {
153
+ var removeMentionById = useCallback(function (id) {
135
154
  var editor = editorRef.current;
136
- var node = Array.from((editor === null || editor === void 0 ? void 0 : editor.querySelectorAll("[".concat(TAG_ATTR, "]"))) || []).find(function (item) {
137
- return item.dataset.xadkInlineTagId === id;
155
+ var node = Array.from((editor === null || editor === void 0 ? void 0 : editor.querySelectorAll("[".concat(MENTION_ATTR, "]"))) || []).find(function (item) {
156
+ return item.dataset.xadkInlineMentionId === id;
138
157
  });
139
158
  if (node) {
140
- removeTagNode(node);
159
+ removeMentionNode(node);
141
160
  }
142
161
  emitChange();
143
162
  editor === null || editor === void 0 || editor.focus();
144
- }, [emitChange, removeTagNode]);
163
+ }, [emitChange, removeMentionNode]);
145
164
 
146
- // 标签节点本身不可编辑;删除入口通过 onRemove 回到组件内部统一维护 DOM 与 tags。
147
- var buildTagNode = useCallback(function (tag) {
165
+ // Mention 节点本身不可编辑;删除入口通过 onRemove 回到组件内部统一维护 DOM 与 mentions。
166
+ var buildMentionNode = useCallback(function (mention) {
148
167
  var span = document.createElement("span");
149
168
  span.setAttribute("contenteditable", "false");
150
- span.setAttribute(TAG_ATTR, tag.id);
151
- span.dataset.xadkInlineTagId = tag.id;
169
+ span.setAttribute(MENTION_ATTR, mention.id);
170
+ span.dataset.xadkInlineMentionId = mention.id;
152
171
  span.className = styles.inlineTag;
153
- if (renderTag) {
172
+ if (renderMention) {
154
173
  var root = createRoot(span);
155
174
  tagRootMapRef.current.set(span, root);
156
175
  root.render( /*#__PURE__*/_jsx(_Fragment, {
157
- children: renderTag(tag, {
176
+ children: renderMention(mention, {
158
177
  onRemove: function onRemove() {
159
- return removeTagById(tag.id);
178
+ return removeMentionById(mention.id);
160
179
  }
161
180
  })
162
181
  }));
163
182
  } else {
164
183
  var label = document.createElement("span");
165
184
  label.className = "xadk-inline-tag-label";
166
- label.textContent = tag.label;
185
+ label.textContent = mention.displayName || mention.label;
167
186
  var close = document.createElement("span");
168
187
  close.className = "xadk-inline-tag-close";
169
188
  close.innerHTML = "×";
170
189
  close.onclick = function (event) {
171
190
  event.preventDefault();
172
191
  event.stopPropagation();
173
- removeTagById(tag.id);
192
+ removeMentionById(mention.id);
174
193
  };
175
194
  span.append(label, close);
176
195
  }
177
196
  return span;
178
- }, [removeTagById, renderTag, styles.inlineTag]);
197
+ }, [removeMentionById, renderMention, styles.inlineTag]);
198
+ var renderPartsToEditor = useCallback(function (parts) {
199
+ var editor = editorRef.current;
200
+ if (!editor) return;
201
+ editor.querySelectorAll("[".concat(MENTION_ATTR, "]")).forEach(function (node) {
202
+ var _tagRootMapRef$curren2;
203
+ (_tagRootMapRef$curren2 = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren2 === void 0 || _tagRootMapRef$curren2.unmount();
204
+ });
205
+ editor.innerHTML = "";
206
+ parts.forEach(function (part) {
207
+ if (part.type === "text") {
208
+ if (part.text) editor.appendChild(createTextNode(part.text));
209
+ return;
210
+ }
211
+ var mention = part.type === "mention" ? part.mention : part.tag;
212
+ editor.append(buildMentionNode(mention), createTextNode(CARET_TEXT));
213
+ });
214
+ }, [buildMentionNode]);
179
215
  var setCursorAtTextEnd = useCallback(function (node) {
180
216
  var _node$textContent;
181
217
  var range = document.createRange();
@@ -184,6 +220,17 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
184
220
  range.collapse(true);
185
221
  selection === null || selection === void 0 || selection.removeAllRanges();
186
222
  selection === null || selection === void 0 || selection.addRange(range);
223
+ savedRangeRef.current = range.cloneRange();
224
+ }, []);
225
+ var saveSelectionRange = useCallback(function () {
226
+ var editor = editorRef.current;
227
+ var selection = window.getSelection();
228
+ if (!editor || !selection || selection.rangeCount === 0) return;
229
+ var range = selection.getRangeAt(0);
230
+ if (!editor.contains(range.startContainer) || !editor.contains(range.endContainer)) {
231
+ return;
232
+ }
233
+ savedRangeRef.current = range.cloneRange();
187
234
  }, []);
188
235
  var restoreCursorAfterInsert = useCallback(function (node) {
189
236
  var editor = editorRef.current;
@@ -191,52 +238,52 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
191
238
  editor.focus();
192
239
  setCursorAtTextEnd(node);
193
240
  }, [setCursorAtTextEnd]);
194
- var isTagNode = function isTagNode(node) {
195
- return node instanceof HTMLElement && node.hasAttribute(TAG_ATTR);
241
+ var isMentionNode = function isMentionNode(node) {
242
+ return node instanceof HTMLElement && node.hasAttribute(MENTION_ATTR);
196
243
  };
197
244
  var isOnlyCaretText = function isOnlyCaretText(text) {
198
245
  return normalizeText(text) === "";
199
246
  };
200
247
 
201
- // Backspace/Delete 需要跨过零宽锚点识别相邻标签,不能只看光标所在文本节点。
202
- var getTagBeforeRange = function getTagBeforeRange(range) {
248
+ // Backspace/Delete 需要跨过零宽锚点识别相邻 Mention,不能只看光标所在文本节点。
249
+ var getMentionBeforeRange = function getMentionBeforeRange(range) {
203
250
  var container = range.startContainer;
204
251
  if (container.nodeType === Node.TEXT_NODE) {
205
252
  var text = container.textContent || "";
206
253
  var beforeCursor = text.slice(0, range.startOffset);
207
- if (isOnlyCaretText(beforeCursor) && isTagNode(container.previousSibling)) {
254
+ if (isOnlyCaretText(beforeCursor) && isMentionNode(container.previousSibling)) {
208
255
  return container.previousSibling;
209
256
  }
210
257
  }
211
258
  if (container instanceof HTMLElement) {
212
259
  var previous = container.childNodes[range.startOffset - 1];
213
- if (isTagNode(previous)) return previous;
214
- if ((previous === null || previous === void 0 ? void 0 : previous.nodeType) === Node.TEXT_NODE && isOnlyCaretText(previous.textContent || "") && isTagNode(previous.previousSibling)) {
260
+ if (isMentionNode(previous)) return previous;
261
+ if ((previous === null || previous === void 0 ? void 0 : previous.nodeType) === Node.TEXT_NODE && isOnlyCaretText(previous.textContent || "") && isMentionNode(previous.previousSibling)) {
215
262
  return previous.previousSibling;
216
263
  }
217
264
  }
218
265
  return null;
219
266
  };
220
- var getTagAfterRange = function getTagAfterRange(range) {
267
+ var getMentionAfterRange = function getMentionAfterRange(range) {
221
268
  var container = range.startContainer;
222
269
  if (container.nodeType === Node.TEXT_NODE) {
223
270
  var text = container.textContent || "";
224
271
  var afterCursor = text.slice(range.startOffset);
225
- if (isOnlyCaretText(afterCursor) && isTagNode(container.nextSibling)) {
272
+ if (isOnlyCaretText(afterCursor) && isMentionNode(container.nextSibling)) {
226
273
  return container.nextSibling;
227
274
  }
228
275
  }
229
276
  if (container instanceof HTMLElement) {
230
277
  var next = container.childNodes[range.startOffset];
231
- if (isTagNode(next)) return next;
232
- if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE && isOnlyCaretText(next.textContent || "") && isTagNode(next.nextSibling)) {
278
+ if (isMentionNode(next)) return next;
279
+ if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE && isOnlyCaretText(next.textContent || "") && isMentionNode(next.nextSibling)) {
233
280
  return next.nextSibling;
234
281
  }
235
282
  }
236
283
  return null;
237
284
  };
238
285
 
239
- // 读取光标在纯文本中的位置,供快捷键搜索词和 tag 插入位置复用。
286
+ // 读取光标在纯文本中的位置,供快捷键搜索词和 Mention 插入位置复用。
240
287
  var getOffsetInEditor = useCallback(function () {
241
288
  var editor = editorRef.current;
242
289
  var selection = window.getSelection();
@@ -253,7 +300,7 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
253
300
  found = true;
254
301
  return;
255
302
  }
256
- if (node instanceof HTMLElement && node.hasAttribute(TAG_ATTR)) return;
303
+ if (node instanceof HTMLElement && node.hasAttribute(MENTION_ATTR)) return;
257
304
  if (node.nodeType === Node.TEXT_NODE) {
258
305
  offset += getPlainLength(node.textContent || "");
259
306
  return;
@@ -273,8 +320,8 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
273
320
  return index;
274
321
  };
275
322
 
276
- // 删除快捷键触发片段,例如把 “@文档” 替换成一个结构化 tag,并返回删除位置对应的真实 DOM Range。
277
- // 注意:纯文本 offset 不包含 tag,标签前后可能都是 offset 0;插入 tag 必须依赖真实 Range,不能再反推 offset。
323
+ // 删除快捷键触发片段,例如把 “@文档” 替换成一个结构化 Mention,并返回删除位置对应的真实 DOM Range。
324
+ // 注意:纯文本 offset 不包含 Mention,Mention 前后可能都是 offset 0;插入 Mention 必须依赖真实 Range,不能再反推 offset。
278
325
  var replaceTextRange = useCallback(function (start, end) {
279
326
  var editor = editorRef.current;
280
327
  if (!editor) return null;
@@ -310,7 +357,7 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
310
357
  return insertRange;
311
358
  }, []);
312
359
 
313
- // 浏览器拆分 Range 后可能留下空文本节点;清理后才能保证后续 tag 继续 append 在队尾。
360
+ // 浏览器拆分 Range 后可能留下空文本节点;清理后才能保证后续 Mention 继续 append 在队尾。
314
361
  var cleanupCaretTextNodes = useCallback(function () {
315
362
  var editor = editorRef.current;
316
363
  if (!editor) return;
@@ -326,43 +373,50 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
326
373
  }
327
374
  });
328
375
  }, []);
329
- var insertTag = useCallback(function (tag, replace) {
376
+ var insertMention = useCallback(function (mention, replace) {
330
377
  var editor = editorRef.current;
331
378
  if (!editor) return;
332
379
  editor.focus();
333
- latestTagsRef.current = [].concat(_toConsumableArray(latestTagsRef.current.filter(function (item) {
334
- return item.id !== tag.id;
335
- })), [tag]);
380
+ latestMentionsRef.current = [].concat(_toConsumableArray(latestMentionsRef.current.filter(function (item) {
381
+ return item.id !== mention.id;
382
+ })), [mention]);
336
383
  var insertRange = null;
337
384
  if (replace) {
338
385
  insertRange = replaceTextRange(replace.start, replace.end);
339
386
  cleanupCaretTextNodes();
340
387
  }
341
- var tagNode = buildTagNode(tag);
388
+ var mentionNode = buildMentionNode(mention);
342
389
  var caretNode = createTextNode(CARET_TEXT);
343
390
  var selection = window.getSelection();
344
- var range = insertRange || (selection !== null && selection !== void 0 && selection.rangeCount ? selection.getRangeAt(0) : null);
391
+ var savedRange = savedRangeRef.current;
392
+ var range = insertRange || (selection !== null && selection !== void 0 && selection.rangeCount && editor.contains(selection.getRangeAt(0).startContainer) ? selection.getRangeAt(0) : savedRange && editor.contains(savedRange.startContainer) ? savedRange : null);
345
393
  if (range && editor.contains(range.startContainer)) {
346
- // 用 DocumentFragment 一次性插入,避免连续 insertNode 导致 tag/caret 顺序被浏览器 Range 状态打乱。
394
+ // 用 DocumentFragment 一次性插入,避免连续 insertNode 导致 mention/caret 顺序被浏览器 Range 状态打乱。
347
395
  var fragment = document.createDocumentFragment();
348
- fragment.append(tagNode, caretNode);
396
+ fragment.append(mentionNode, caretNode);
349
397
  range.insertNode(fragment);
350
398
  } else {
351
- editor.append(tagNode, caretNode);
399
+ editor.append(mentionNode, caretNode);
352
400
  }
353
401
  cleanupCaretTextNodes();
354
402
  lastInsertedCaretRef.current = caretNode;
355
403
  setCursorAtTextEnd(caretNode);
356
404
  emitChange();
357
405
 
358
- // 关闭快捷列表和父组件同步 tags/value 会触发一次 React 更新;
359
- // 下一帧再恢复到刚插入 tag 后的零宽锚点,避免光标被 focus 或重渲染带回旧位置。
406
+ // 关闭快捷列表和父组件同步 mentions/value 会触发一次 React 更新;
407
+ // 下一帧再恢复到刚插入 Mention 后的零宽锚点,避免光标被 focus 或重渲染带回旧位置。
360
408
  window.requestAnimationFrame(function () {
361
409
  if (lastInsertedCaretRef.current === caretNode) {
362
410
  restoreCursorAfterInsert(caretNode);
363
411
  }
364
412
  });
365
- }, [buildTagNode, cleanupCaretTextNodes, emitChange, replaceTextRange, restoreCursorAfterInsert, setCursorAtTextEnd]);
413
+ }, [buildMentionNode, cleanupCaretTextNodes, emitChange, replaceTextRange, restoreCursorAfterInsert, setCursorAtTextEnd]);
414
+ useEffect(function () {
415
+ document.addEventListener("selectionchange", saveSelectionRange);
416
+ return function () {
417
+ document.removeEventListener("selectionchange", saveSelectionRange);
418
+ };
419
+ }, [saveSelectionRange]);
366
420
  useImperativeHandle(ref, function () {
367
421
  return {
368
422
  focus: function focus() {
@@ -372,51 +426,56 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
372
426
  getText: getPlainText,
373
427
  getParts: getParts,
374
428
  getCursorOffset: getOffsetInEditor,
375
- insertTag: insertTag
429
+ insertMention: insertMention,
430
+ insertTag: insertMention
376
431
  };
377
- }, [getOffsetInEditor, getParts, getPlainText, insertTag]);
378
- useEffect(function () {
379
- var editor = editorRef.current;
380
- if (!editor || editor.childNodes.length > 0) return;
381
- if (value) editor.appendChild(createTextNode(value));
382
- tags.forEach(function (tag) {
383
- return editor.append(buildTagNode(tag), createTextNode(CARET_TEXT));
384
- });
385
- }, []);
386
-
387
- // 外部受控 tags 变化时,同步补齐或删除 DOM 中的 tag 节点。
388
- useEffect(function () {
432
+ }, [getOffsetInEditor, getParts, getPlainText, insertMention]);
433
+ useIsomorphicLayoutEffect(function () {
389
434
  var editor = editorRef.current;
390
435
  if (!editor) return;
391
- var existingNodes = Array.from(editor.querySelectorAll("[".concat(TAG_ATTR, "]")));
392
- var nextTagMap = new Map(tags.map(function (tag) {
393
- return [tag.id, tag];
394
- }));
395
- var existingIds = new Set(existingNodes.map(function (node) {
396
- return node.dataset.xadkInlineTagId || "";
397
- }));
398
- existingNodes.forEach(function (node) {
399
- var tagId = node.dataset.xadkInlineTagId || "";
400
- if (!nextTagMap.has(tagId)) {
401
- removeTagNode(node);
402
- }
403
- });
404
- tags.forEach(function (tag) {
405
- if (existingIds.has(tag.id)) return;
406
- editor.append(buildTagNode(tag), createTextNode(CARET_TEXT));
436
+
437
+ // 默认片段只在空输入首次挂载时灌入,避免外部重新渲染覆盖用户正在编辑的内容。
438
+ if (!defaultInputPartsAppliedRef.current && defaultInputParts !== null && defaultInputParts !== void 0 && defaultInputParts.length && !value && mentions.length === 0) {
439
+ defaultInputPartsAppliedRef.current = true;
440
+ renderPartsToEditor(defaultInputParts);
441
+ emitChange();
442
+ return;
443
+ }
444
+ var incomingMentionIds = mentions.map(function (mention) {
445
+ return mention.id;
446
+ }).join("|");
447
+ var lastEmittedState = lastEmittedStateRef.current;
448
+ var isInternalControlledEcho = value === lastEmittedState.value && incomingMentionIds === lastEmittedState.mentionIds;
449
+ if (isInternalControlledEcho && editor.childNodes.length > 0) return;
450
+ renderPartsToEditor([].concat(_toConsumableArray(value ? [{
451
+ type: "text",
452
+ text: value
453
+ }] : []), _toConsumableArray(mentions.map(function (mention) {
454
+ return {
455
+ type: "mention",
456
+ mention: mention
457
+ };
458
+ }))));
459
+
460
+ // 外部主动写入 value/mentions 后,需要在首帧绘制前同步 DOM;若输入区已有焦点,则把光标放在内容末尾。
461
+ var lastTextNode = Array.from(editor.childNodes).reverse().find(function (node) {
462
+ return node.nodeType === Node.TEXT_NODE;
407
463
  });
408
- }, [buildTagNode, removeTagNode, tags]);
409
- useEffect(function () {
464
+ if (document.activeElement === editor && lastTextNode) {
465
+ setCursorAtTextEnd(lastTextNode);
466
+ }
467
+ }, [defaultInputParts, emitChange, renderPartsToEditor, setCursorAtTextEnd, mentions, value]);
468
+ useIsomorphicLayoutEffect(function () {
410
469
  var editor = editorRef.current;
411
470
  if (!editor) return;
412
- if (!value && tags.length === 0 && editor.childNodes.length > 0) {
413
- editor.querySelectorAll("[".concat(TAG_ATTR, "]")).forEach(function (node) {
414
- var _tagRootMapRef$curren2;
415
- (_tagRootMapRef$curren2 = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren2 === void 0 || _tagRootMapRef$curren2.unmount();
471
+ if (!value && mentions.length === 0 && editor.childNodes.length > 0) {
472
+ editor.querySelectorAll("[".concat(MENTION_ATTR, "]")).forEach(function (node) {
473
+ var _tagRootMapRef$curren3;
474
+ (_tagRootMapRef$curren3 = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren3 === void 0 || _tagRootMapRef$curren3.unmount();
416
475
  });
417
476
  editor.innerHTML = "";
418
477
  }
419
- }, [tags.length, value]);
478
+ }, [mentions.length, value]);
420
479
  var handleInput = function handleInput() {
421
480
  if (!composingRef.current) emitChange();
422
481
  };
@@ -426,10 +485,10 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
426
485
  if (selection !== null && selection !== void 0 && selection.rangeCount) {
427
486
  var range = selection.getRangeAt(0);
428
487
  if (range.collapsed) {
429
- var targetTag = event.key === "Backspace" ? getTagBeforeRange(range) : getTagAfterRange(range);
430
- if (targetTag) {
488
+ var targetMention = event.key === "Backspace" ? getMentionBeforeRange(range) : getMentionAfterRange(range);
489
+ if (targetMention) {
431
490
  event.preventDefault();
432
- removeTagById(targetTag.dataset.xadkInlineTagId || "");
491
+ removeMentionById(targetMention.dataset.xadkInlineMentionId || "");
433
492
  return;
434
493
  }
435
494
  }
@@ -437,19 +496,29 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
437
496
  }
438
497
  onKeyDown === null || onKeyDown === void 0 || onKeyDown(event);
439
498
  };
440
- return /*#__PURE__*/_jsx("div", {
499
+
500
+ // placeholder 只由受控 value/mentions 派生,避免从 contenteditable DOM 读取导致外部插入 Mention 后状态滞后一帧。
501
+ var showPlaceholder = !value && mentions.length === 0;
502
+ return /*#__PURE__*/_jsxs("div", {
441
503
  className: styles.inlineInputWrapper,
442
- children: /*#__PURE__*/_jsx("div", {
504
+ children: [showPlaceholder && placeholder && /*#__PURE__*/_jsx("div", {
505
+ className: styles.inlinePlaceholder,
506
+ children: placeholder
507
+ }), /*#__PURE__*/_jsx("div", {
443
508
  ref: editorRef,
444
509
  className: styles.inlineInput,
445
- "data-placeholder": placeholder,
446
510
  contentEditable: !disabled,
447
511
  suppressContentEditableWarning: true,
448
512
  role: "textbox",
449
513
  "aria-multiline": "true",
450
514
  onInput: handleInput,
451
515
  onKeyDown: handleKeyDown,
452
- onKeyUp: onKeyUp,
516
+ onKeyUp: function onKeyUp(event) {
517
+ saveSelectionRange();
518
+ _onKeyUp === null || _onKeyUp === void 0 || _onKeyUp(event);
519
+ },
520
+ onMouseUp: saveSelectionRange,
521
+ onFocus: saveSelectionRange,
453
522
  onCompositionStart: function onCompositionStart() {
454
523
  composingRef.current = true;
455
524
  },
@@ -457,9 +526,8 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
457
526
  composingRef.current = false;
458
527
  emitChange();
459
528
  },
460
- "data-empty": !getPlainText() && getOrderedTags().length === 0,
461
529
  "data-cursor-offset": getOffsetInEditor()
462
- })
530
+ })]
463
531
  });
464
532
  });
465
533
  InlineTagInput.displayName = "InlineTagInput";