@ai-group/chat-sdk 3.7.1-alpha1 → 3.7.1-alpha10

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 (221) hide show
  1. package/README.md +22 -0
  2. package/dist/cjs/components/FileGallery/index.js +59 -40
  3. package/dist/cjs/components/FileGallery/index.js.map +2 -2
  4. package/dist/cjs/components/FileGallery/styles.d.ts +7 -0
  5. package/dist/cjs/components/FileGallery/styles.js +78 -0
  6. package/dist/cjs/components/FileGallery/styles.js.map +2 -2
  7. package/dist/cjs/components/Sources/components/CarouselCard.d.ts +12 -0
  8. package/dist/cjs/components/Sources/components/CarouselCard.js +130 -0
  9. package/dist/cjs/components/Sources/components/CarouselCard.js.map +7 -0
  10. package/dist/cjs/components/Sources/index.d.ts +5 -0
  11. package/dist/cjs/components/Sources/index.js +162 -0
  12. package/dist/cjs/components/Sources/index.js.map +7 -0
  13. package/dist/cjs/components/Sources/styles.d.ts +59 -0
  14. package/dist/cjs/components/Sources/styles.js +179 -0
  15. package/dist/cjs/components/Sources/styles.js.map +7 -0
  16. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/index.d.ts +19 -1
  17. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/index.js +232 -22
  18. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/index.js.map +2 -2
  19. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/styles.d.ts +3 -0
  20. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/styles.js +83 -0
  21. package/dist/cjs/components/XAdkChatbot/components/MarkdownRender/styles.js.map +2 -2
  22. package/dist/cjs/components/XAdkChatbot/index.d.ts +2 -2
  23. package/dist/cjs/components/XAdkChatbot/index.js +75 -8
  24. package/dist/cjs/components/XAdkChatbot/index.js.map +2 -2
  25. package/dist/cjs/components/XAdkChatbot/styles.js +1 -1
  26. package/dist/cjs/components/XAdkChatbot/styles.js.map +1 -1
  27. package/dist/cjs/components/XAdkProvider/compound/DefaultLayout.d.ts +7 -2
  28. package/dist/cjs/components/XAdkProvider/compound/DefaultLayout.js +30 -12
  29. package/dist/cjs/components/XAdkProvider/compound/DefaultLayout.js.map +2 -2
  30. package/dist/cjs/components/XAdkProvider/compound/Sender.d.ts +3 -1
  31. package/dist/cjs/components/XAdkProvider/compound/Sender.js +81 -4
  32. package/dist/cjs/components/XAdkProvider/compound/Sender.js.map +3 -3
  33. package/dist/cjs/components/XAdkProvider/compound/styles.d.ts +1 -0
  34. package/dist/cjs/components/XAdkProvider/compound/styles.js +16 -0
  35. package/dist/cjs/components/XAdkProvider/compound/styles.js.map +2 -2
  36. package/dist/cjs/components/XAdkProvider/context/ChatSenderStateContext.d.ts +4 -0
  37. package/dist/cjs/components/XAdkProvider/context/ChatSenderStateContext.js.map +2 -2
  38. package/dist/cjs/components/XAdkProvider/context/DefaultLayoutContext.d.ts +9 -0
  39. package/dist/cjs/components/XAdkProvider/context/DefaultLayoutContext.js +34 -0
  40. package/dist/cjs/components/XAdkProvider/context/DefaultLayoutContext.js.map +7 -0
  41. package/dist/cjs/components/XAdkProvider/context/index.d.ts +5 -4
  42. package/dist/cjs/components/XAdkProvider/context/index.js +3 -1
  43. package/dist/cjs/components/XAdkProvider/context/index.js.map +2 -2
  44. package/dist/cjs/components/XAdkProvider/index.js +11 -10
  45. package/dist/cjs/components/XAdkProvider/index.js.map +2 -2
  46. package/dist/cjs/components/XAdkSender/InlineTagInput.d.ts +12 -5
  47. package/dist/cjs/components/XAdkSender/InlineTagInput.js +419 -121
  48. package/dist/cjs/components/XAdkSender/InlineTagInput.js.map +2 -2
  49. package/dist/cjs/components/XAdkSender/InputTags.d.ts +2 -2
  50. package/dist/cjs/components/XAdkSender/InputTags.js +8 -8
  51. package/dist/cjs/components/XAdkSender/InputTags.js.map +2 -2
  52. package/dist/cjs/components/XAdkSender/index.js +173 -112
  53. package/dist/cjs/components/XAdkSender/index.js.map +3 -3
  54. package/dist/cjs/components/XAdkSender/styles.d.ts +4 -0
  55. package/dist/cjs/components/XAdkSender/styles.js +47 -6
  56. package/dist/cjs/components/XAdkSender/styles.js.map +2 -2
  57. package/dist/cjs/components/XAiConversations/styles.js +11 -1
  58. package/dist/cjs/components/XAiConversations/styles.js.map +2 -2
  59. package/dist/cjs/hooks/useADKChat.js +5 -1
  60. package/dist/cjs/hooks/useADKChat.js.map +2 -2
  61. package/dist/cjs/index.d.ts +8 -2
  62. package/dist/cjs/index.js +44 -7
  63. package/dist/cjs/index.js.map +3 -3
  64. package/dist/cjs/presets/base.d.ts +6 -0
  65. package/dist/cjs/presets/base.js +50 -0
  66. package/dist/cjs/presets/base.js.map +7 -0
  67. package/dist/cjs/presets/define.d.ts +11 -0
  68. package/dist/cjs/presets/define.js +58 -0
  69. package/dist/cjs/presets/define.js.map +7 -0
  70. package/dist/cjs/presets/index.d.ts +3 -0
  71. package/dist/cjs/presets/index.js +28 -0
  72. package/dist/cjs/presets/index.js.map +7 -0
  73. package/dist/cjs/presets/runtime.d.ts +22 -0
  74. package/dist/cjs/presets/runtime.js +75 -0
  75. package/dist/cjs/presets/runtime.js.map +7 -0
  76. package/dist/cjs/presets/xgroup/index.d.ts +5 -0
  77. package/dist/cjs/presets/xgroup/index.js +10 -0
  78. package/dist/cjs/presets/xgroup/index.js.map +2 -2
  79. package/dist/cjs/presets/xgroup/knowledgeReference.d.ts +17 -0
  80. package/dist/cjs/presets/xgroup/knowledgeReference.js +162 -0
  81. package/dist/cjs/presets/xgroup/knowledgeReference.js.map +7 -0
  82. package/dist/cjs/presets/xgroup/preset.d.ts +1 -13
  83. package/dist/cjs/presets/xgroup/preset.js +16 -37
  84. package/dist/cjs/presets/xgroup/preset.js.map +2 -2
  85. package/dist/cjs/presets/xgroup/strategies.d.ts +5 -4
  86. package/dist/cjs/presets/xgroup/strategies.js +11 -15
  87. package/dist/cjs/presets/xgroup/strategies.js.map +3 -3
  88. package/dist/cjs/presets/xgroup/webReference.d.ts +10 -0
  89. package/dist/cjs/presets/xgroup/webReference.js +85 -0
  90. package/dist/cjs/presets/xgroup/webReference.js.map +7 -0
  91. package/dist/cjs/types/ChatStrategies.d.ts +11 -2
  92. package/dist/cjs/types/ChatStrategies.js.map +1 -1
  93. package/dist/cjs/types/FileGallery.d.ts +3 -0
  94. package/dist/cjs/types/FileGallery.js.map +1 -1
  95. package/dist/cjs/types/Sources.d.ts +30 -0
  96. package/dist/cjs/types/Sources.js +18 -0
  97. package/dist/cjs/types/Sources.js.map +7 -0
  98. package/dist/cjs/types/XAdkChatbot.d.ts +33 -0
  99. package/dist/cjs/types/XAdkChatbot.js.map +1 -1
  100. package/dist/cjs/types/XAdkSender.d.ts +112 -21
  101. package/dist/cjs/types/XAdkSender.js.map +1 -1
  102. package/dist/cjs/types/index.d.ts +1 -0
  103. package/dist/cjs/types/index.js +3 -1
  104. package/dist/cjs/types/index.js.map +2 -2
  105. package/dist/cjs/utils/index.d.ts +1 -0
  106. package/dist/cjs/utils/index.js +3 -1
  107. package/dist/cjs/utils/index.js.map +2 -2
  108. package/dist/cjs/utils/markdownResource.d.ts +14 -0
  109. package/dist/cjs/utils/markdownResource.js +131 -0
  110. package/dist/cjs/utils/markdownResource.js.map +7 -0
  111. package/dist/esm/components/FileGallery/index.js +22 -16
  112. package/dist/esm/components/FileGallery/index.js.map +1 -1
  113. package/dist/esm/components/FileGallery/styles.d.ts +7 -0
  114. package/dist/esm/components/FileGallery/styles.js +24 -17
  115. package/dist/esm/components/FileGallery/styles.js.map +1 -1
  116. package/dist/esm/components/Sources/components/CarouselCard.d.ts +12 -0
  117. package/dist/esm/components/Sources/components/CarouselCard.js +107 -0
  118. package/dist/esm/components/Sources/components/CarouselCard.js.map +1 -0
  119. package/dist/esm/components/Sources/index.d.ts +5 -0
  120. package/dist/esm/components/Sources/index.js +127 -0
  121. package/dist/esm/components/Sources/index.js.map +1 -0
  122. package/dist/esm/components/Sources/styles.d.ts +59 -0
  123. package/dist/esm/components/Sources/styles.js +34 -0
  124. package/dist/esm/components/Sources/styles.js.map +1 -0
  125. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/index.d.ts +19 -1
  126. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/index.js +190 -11
  127. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/index.js.map +1 -1
  128. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/styles.d.ts +3 -0
  129. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/styles.js +5 -2
  130. package/dist/esm/components/XAdkChatbot/components/MarkdownRender/styles.js.map +1 -1
  131. package/dist/esm/components/XAdkChatbot/index.d.ts +2 -2
  132. package/dist/esm/components/XAdkChatbot/index.js +55 -10
  133. package/dist/esm/components/XAdkChatbot/index.js.map +1 -1
  134. package/dist/esm/components/XAdkChatbot/styles.js +1 -1
  135. package/dist/esm/components/XAdkChatbot/styles.js.map +1 -1
  136. package/dist/esm/components/XAdkProvider/compound/DefaultLayout.d.ts +7 -2
  137. package/dist/esm/components/XAdkProvider/compound/DefaultLayout.js +40 -17
  138. package/dist/esm/components/XAdkProvider/compound/DefaultLayout.js.map +1 -1
  139. package/dist/esm/components/XAdkProvider/compound/Sender.d.ts +3 -1
  140. package/dist/esm/components/XAdkProvider/compound/Sender.js +70 -6
  141. package/dist/esm/components/XAdkProvider/compound/Sender.js.map +1 -1
  142. package/dist/esm/components/XAdkProvider/compound/styles.d.ts +1 -0
  143. package/dist/esm/components/XAdkProvider/compound/styles.js +3 -2
  144. package/dist/esm/components/XAdkProvider/compound/styles.js.map +1 -1
  145. package/dist/esm/components/XAdkProvider/context/ChatSenderStateContext.d.ts +4 -0
  146. package/dist/esm/components/XAdkProvider/context/ChatSenderStateContext.js.map +1 -1
  147. package/dist/esm/components/XAdkProvider/context/DefaultLayoutContext.d.ts +9 -0
  148. package/dist/esm/components/XAdkProvider/context/DefaultLayoutContext.js +9 -0
  149. package/dist/esm/components/XAdkProvider/context/DefaultLayoutContext.js.map +1 -0
  150. package/dist/esm/components/XAdkProvider/context/index.d.ts +5 -4
  151. package/dist/esm/components/XAdkProvider/context/index.js +1 -0
  152. package/dist/esm/components/XAdkProvider/context/index.js.map +1 -1
  153. package/dist/esm/components/XAdkProvider/index.js +7 -3
  154. package/dist/esm/components/XAdkProvider/index.js.map +1 -1
  155. package/dist/esm/components/XAdkSender/InlineTagInput.d.ts +12 -5
  156. package/dist/esm/components/XAdkSender/InlineTagInput.js +408 -111
  157. package/dist/esm/components/XAdkSender/InlineTagInput.js.map +1 -1
  158. package/dist/esm/components/XAdkSender/InputTags.d.ts +2 -2
  159. package/dist/esm/components/XAdkSender/InputTags.js +10 -9
  160. package/dist/esm/components/XAdkSender/InputTags.js.map +1 -1
  161. package/dist/esm/components/XAdkSender/index.js +206 -122
  162. package/dist/esm/components/XAdkSender/index.js.map +1 -1
  163. package/dist/esm/components/XAdkSender/styles.d.ts +4 -0
  164. package/dist/esm/components/XAdkSender/styles.js +29 -25
  165. package/dist/esm/components/XAdkSender/styles.js.map +1 -1
  166. package/dist/esm/components/XAiConversations/styles.js +3 -3
  167. package/dist/esm/components/XAiConversations/styles.js.map +1 -1
  168. package/dist/esm/hooks/useADKChat.js +8 -2
  169. package/dist/esm/hooks/useADKChat.js.map +1 -1
  170. package/dist/esm/index.d.ts +8 -2
  171. package/dist/esm/index.js +7 -3
  172. package/dist/esm/index.js.map +1 -1
  173. package/dist/esm/presets/base.d.ts +6 -0
  174. package/dist/esm/presets/base.js +28 -0
  175. package/dist/esm/presets/base.js.map +1 -0
  176. package/dist/esm/presets/define.d.ts +11 -0
  177. package/dist/esm/presets/define.js +33 -0
  178. package/dist/esm/presets/define.js.map +1 -0
  179. package/dist/esm/presets/index.d.ts +3 -0
  180. package/dist/esm/presets/index.js +4 -0
  181. package/dist/esm/presets/index.js.map +1 -0
  182. package/dist/esm/presets/runtime.d.ts +22 -0
  183. package/dist/esm/presets/runtime.js +45 -0
  184. package/dist/esm/presets/runtime.js.map +1 -0
  185. package/dist/esm/presets/xgroup/index.d.ts +5 -0
  186. package/dist/esm/presets/xgroup/index.js +6 -0
  187. package/dist/esm/presets/xgroup/index.js.map +1 -1
  188. package/dist/esm/presets/xgroup/knowledgeReference.d.ts +17 -0
  189. package/dist/esm/presets/xgroup/knowledgeReference.js +95 -0
  190. package/dist/esm/presets/xgroup/knowledgeReference.js.map +1 -0
  191. package/dist/esm/presets/xgroup/preset.d.ts +1 -13
  192. package/dist/esm/presets/xgroup/preset.js +9 -29
  193. package/dist/esm/presets/xgroup/preset.js.map +1 -1
  194. package/dist/esm/presets/xgroup/strategies.d.ts +5 -4
  195. package/dist/esm/presets/xgroup/strategies.js +8 -20
  196. package/dist/esm/presets/xgroup/strategies.js.map +1 -1
  197. package/dist/esm/presets/xgroup/webReference.d.ts +10 -0
  198. package/dist/esm/presets/xgroup/webReference.js +46 -0
  199. package/dist/esm/presets/xgroup/webReference.js.map +1 -0
  200. package/dist/esm/types/ChatStrategies.d.ts +11 -2
  201. package/dist/esm/types/ChatStrategies.js.map +1 -1
  202. package/dist/esm/types/FileGallery.d.ts +3 -0
  203. package/dist/esm/types/FileGallery.js.map +1 -1
  204. package/dist/esm/types/Sources.d.ts +30 -0
  205. package/dist/esm/types/Sources.js +2 -0
  206. package/dist/esm/types/Sources.js.map +1 -0
  207. package/dist/esm/types/XAdkChatbot.d.ts +33 -0
  208. package/dist/esm/types/XAdkChatbot.js.map +1 -1
  209. package/dist/esm/types/XAdkSender.d.ts +112 -21
  210. package/dist/esm/types/XAdkSender.js.map +1 -1
  211. package/dist/esm/types/index.d.ts +1 -0
  212. package/dist/esm/types/index.js +1 -0
  213. package/dist/esm/types/index.js.map +1 -1
  214. package/dist/esm/utils/index.d.ts +1 -0
  215. package/dist/esm/utils/index.js +1 -0
  216. package/dist/esm/utils/index.js.map +1 -1
  217. package/dist/esm/utils/markdownResource.d.ts +14 -0
  218. package/dist/esm/utils/markdownResource.js +101 -0
  219. package/dist/esm/utils/markdownResource.js.map +1 -0
  220. package/dist/umd/chat-sdk.min.js +1 -1
  221. package/package.json +6 -1
@@ -1,15 +1,20 @@
1
+ import _createForOfIteratorHelper from "@babel/runtime/helpers/esm/createForOfIteratorHelper";
1
2
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
2
- import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef } from "react";
3
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
4
+ import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useImperativeHandle, useRef, useState } from "react";
3
5
  import { createRoot } from "react-dom/client";
4
6
  import { useStyles } from "./styles";
7
+ import { parseMarkdownResourceLinksAsParts } from "../../utils/markdownResource";
5
8
  import { Fragment as _Fragment } from "react/jsx-runtime";
6
9
  import { jsx as _jsx } from "react/jsx-runtime";
7
- var TAG_ATTR = "data-xadk-inline-tag-id";
8
- // contenteditable 无法把光标放进 contenteditable=false 的标签里,需用零宽字符承接“标签后方”的光标。
10
+ import { jsxs as _jsxs } from "react/jsx-runtime";
11
+ var MENTION_ATTR = "data-xadk-inline-mention-id";
12
+ // contenteditable 无法把光标放进 contenteditable=false 的 Mention 里,需用零宽字符承接“Mention 后方”的光标。
9
13
  var CARET_TEXT = "\u200B";
10
14
  var createTextNode = function createTextNode(text) {
11
15
  return document.createTextNode(text);
12
16
  };
17
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
13
18
  var normalizeText = function normalizeText(text) {
14
19
  return text.replace(/\u200B/g, "").replace(/\u00a0/g, " ");
15
20
  };
@@ -25,33 +30,52 @@ var toRawOffset = function toRawOffset(text, plainOffset) {
25
30
  }
26
31
  return text.length;
27
32
  };
33
+
34
+ // 内联标签展示逻辑
28
35
  var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
29
36
  var value = _ref.value,
30
- tags = _ref.tags,
37
+ mentions = _ref.mentions,
38
+ defaultInputParts = _ref.defaultInputParts,
31
39
  placeholder = _ref.placeholder,
32
40
  disabled = _ref.disabled,
33
41
  onChange = _ref.onChange,
34
- onTagsChange = _ref.onTagsChange,
42
+ onMentionsChange = _ref.onMentionsChange,
35
43
  onKeyDown = _ref.onKeyDown,
36
- onKeyUp = _ref.onKeyUp,
37
- renderTag = _ref.renderTag;
44
+ _onKeyUp = _ref.onKeyUp,
45
+ renderMention = _ref.renderMention;
38
46
  var styles = useStyles();
39
47
  var editorRef = useRef(null);
40
- var latestTagsRef = useRef(tags);
48
+ var latestMentionsRef = useRef(mentions);
41
49
  var tagRootMapRef = useRef(new WeakMap());
42
50
  var composingRef = useRef(false);
43
51
  var lastInsertedCaretRef = useRef(null);
52
+ var savedRangeRef = useRef(null);
53
+ var defaultInputPartsAppliedRef = useRef(false);
54
+ var skipEmptyCleanupOnceRef = useRef(false);
55
+ var structuralCleanupFrameRef = useRef(null);
56
+ var _useState = useState(function () {
57
+ return Boolean(value || mentions.length);
58
+ }),
59
+ _useState2 = _slicedToArray(_useState, 2),
60
+ hasContent = _useState2[0],
61
+ setHasContent = _useState2[1];
62
+ var lastEmittedStateRef = useRef({
63
+ value: value,
64
+ mentionIds: mentions.map(function (mention) {
65
+ return mention.id;
66
+ }).join("|")
67
+ });
44
68
  useEffect(function () {
45
- latestTagsRef.current = tags;
46
- }, [tags]);
47
- var getOrderedTags = useCallback(function () {
69
+ latestMentionsRef.current = mentions;
70
+ }, [mentions]);
71
+ var getOrderedMentions = useCallback(function () {
48
72
  var editor = editorRef.current;
49
73
  if (!editor) return [];
50
- var tagMap = new Map(latestTagsRef.current.map(function (tag) {
51
- return [tag.id, tag];
74
+ var mentionMap = new Map(latestMentionsRef.current.map(function (mention) {
75
+ return [mention.id, mention];
52
76
  }));
53
- return Array.from(editor.querySelectorAll("[".concat(TAG_ATTR, "]"))).map(function (node) {
54
- return tagMap.get(node.dataset.xadkInlineTagId || "");
77
+ return Array.from(editor.querySelectorAll("[".concat(MENTION_ATTR, "]"))).map(function (node) {
78
+ return mentionMap.get(node.dataset.xadkInlineMentionId || "");
55
79
  }).filter(Boolean);
56
80
  }, []);
57
81
 
@@ -67,23 +91,61 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
67
91
  });
68
92
  return normalizeText(text);
69
93
  }, []);
94
+
95
+ // 提交时需要保留文本与 Mention 的真实顺序;仅靠 value + mentions 无法区分 Mention 在句首、句中还是句尾。
96
+ var getParts = useCallback(function () {
97
+ var editor = editorRef.current;
98
+ if (!editor) return [];
99
+ var mentionMap = new Map(latestMentionsRef.current.map(function (mention) {
100
+ return [mention.id, mention];
101
+ }));
102
+ var parts = [];
103
+ editor.childNodes.forEach(function (node) {
104
+ if (node.nodeType === Node.TEXT_NODE) {
105
+ var text = normalizeText(node.textContent || "");
106
+ if (text) parts.push({
107
+ type: "text",
108
+ text: text
109
+ });
110
+ return;
111
+ }
112
+ if (node instanceof HTMLElement && node.hasAttribute(MENTION_ATTR)) {
113
+ var mentionId = node.dataset.xadkInlineMentionId || "";
114
+ var _mention = mentionMap.get(mentionId);
115
+ if (_mention) parts.push({
116
+ type: "mention",
117
+ mention: _mention
118
+ });
119
+ }
120
+ });
121
+ return parts;
122
+ }, []);
70
123
  var emitChange = useCallback(function () {
71
- onChange(getPlainText());
72
- onTagsChange(getOrderedTags());
73
- }, [getOrderedTags, getPlainText, onChange, onTagsChange]);
124
+ var nextValue = getPlainText();
125
+ var nextMentions = getOrderedMentions();
126
+ setHasContent(Boolean(nextValue || nextMentions.length));
127
+ lastEmittedStateRef.current = {
128
+ value: nextValue,
129
+ mentionIds: nextMentions.map(function (mention) {
130
+ return mention.id;
131
+ }).join("|")
132
+ };
133
+ onChange(nextValue);
134
+ onMentionsChange(nextMentions);
135
+ }, [getOrderedMentions, getPlainText, onChange, onMentionsChange]);
74
136
  var removeTextRange = useCallback(function (node, start, end) {
75
137
  var text = node.textContent || "";
76
138
  node.textContent = "".concat(text.slice(0, start)).concat(text.slice(end));
77
139
  if (!node.textContent) node.remove();
78
140
  }, []);
79
- var removeTagNode = useCallback(function (node) {
141
+ var removeMentionNode = useCallback(function (node) {
80
142
  var _tagRootMapRef$curren;
81
143
  var previous = node.previousSibling;
82
144
  var next = node.nextSibling;
83
145
  (_tagRootMapRef$curren = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren === void 0 || _tagRootMapRef$curren.unmount();
84
146
  node.remove();
85
147
 
86
- // 标签后会补一个不可见光标锚点;删除标签时同步清理,避免留下冗余节点。
148
+ // Mention 后会补一个不可见光标锚点;删除 Mention 时同步清理,避免留下冗余节点。
87
149
  if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE) {
88
150
  var _text$match;
89
151
  var text = next.textContent || "";
@@ -102,51 +164,69 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
102
164
  }
103
165
  }
104
166
  }, [removeTextRange]);
105
- var removeTagById = useCallback(function (id) {
167
+ var removeMentionById = useCallback(function (id) {
106
168
  var editor = editorRef.current;
107
- var node = Array.from((editor === null || editor === void 0 ? void 0 : editor.querySelectorAll("[".concat(TAG_ATTR, "]"))) || []).find(function (item) {
108
- return item.dataset.xadkInlineTagId === id;
169
+ var node = Array.from((editor === null || editor === void 0 ? void 0 : editor.querySelectorAll("[".concat(MENTION_ATTR, "]"))) || []).find(function (item) {
170
+ return item.dataset.xadkInlineMentionId === id;
109
171
  });
110
172
  if (node) {
111
- removeTagNode(node);
173
+ removeMentionNode(node);
112
174
  }
113
175
  emitChange();
114
176
  editor === null || editor === void 0 || editor.focus();
115
- }, [emitChange, removeTagNode]);
177
+ }, [emitChange, removeMentionNode]);
116
178
 
117
- // 标签节点本身不可编辑;删除入口通过 onRemove 回到组件内部统一维护 DOM 与 tags。
118
- var buildTagNode = useCallback(function (tag) {
179
+ // Mention 节点本身不可编辑;删除入口通过 onRemove 回到组件内部统一维护 DOM 与 mentions。
180
+ var buildMentionNode = useCallback(function (mention) {
119
181
  var span = document.createElement("span");
120
182
  span.setAttribute("contenteditable", "false");
121
- span.setAttribute(TAG_ATTR, tag.id);
122
- span.dataset.xadkInlineTagId = tag.id;
183
+ span.setAttribute(MENTION_ATTR, mention.id);
184
+ span.dataset.xadkInlineMentionId = mention.id;
185
+ span.dataset.xadkInlineMentionType = mention.type;
123
186
  span.className = styles.inlineTag;
124
- if (renderTag) {
187
+ if (renderMention) {
125
188
  var root = createRoot(span);
126
189
  tagRootMapRef.current.set(span, root);
127
190
  root.render( /*#__PURE__*/_jsx(_Fragment, {
128
- children: renderTag(tag, {
191
+ children: renderMention(mention, {
129
192
  onRemove: function onRemove() {
130
- return removeTagById(tag.id);
193
+ return removeMentionById(mention.id);
131
194
  }
132
195
  })
133
196
  }));
134
197
  } else {
135
198
  var label = document.createElement("span");
136
199
  label.className = "xadk-inline-tag-label";
137
- label.textContent = tag.label;
200
+ label.textContent = mention.displayName || mention.label;
138
201
  var close = document.createElement("span");
139
202
  close.className = "xadk-inline-tag-close";
140
203
  close.innerHTML = "×";
141
204
  close.onclick = function (event) {
142
205
  event.preventDefault();
143
206
  event.stopPropagation();
144
- removeTagById(tag.id);
207
+ removeMentionById(mention.id);
145
208
  };
146
209
  span.append(label, close);
147
210
  }
148
211
  return span;
149
- }, [removeTagById, renderTag, styles.inlineTag]);
212
+ }, [removeMentionById, renderMention, styles.inlineTag]);
213
+ var renderPartsToEditor = useCallback(function (parts) {
214
+ var editor = editorRef.current;
215
+ if (!editor) return;
216
+ editor.querySelectorAll("[".concat(MENTION_ATTR, "]")).forEach(function (node) {
217
+ var _tagRootMapRef$curren2;
218
+ (_tagRootMapRef$curren2 = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren2 === void 0 || _tagRootMapRef$curren2.unmount();
219
+ });
220
+ editor.innerHTML = "";
221
+ parts.forEach(function (part) {
222
+ if (part.type === "text") {
223
+ if (part.text) editor.appendChild(createTextNode(part.text));
224
+ return;
225
+ }
226
+ var mention = part.type === "mention" ? part.mention : part.tag;
227
+ editor.append(buildMentionNode(mention), createTextNode(CARET_TEXT));
228
+ });
229
+ }, [buildMentionNode]);
150
230
  var setCursorAtTextEnd = useCallback(function (node) {
151
231
  var _node$textContent;
152
232
  var range = document.createRange();
@@ -155,6 +235,17 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
155
235
  range.collapse(true);
156
236
  selection === null || selection === void 0 || selection.removeAllRanges();
157
237
  selection === null || selection === void 0 || selection.addRange(range);
238
+ savedRangeRef.current = range.cloneRange();
239
+ }, []);
240
+ var saveSelectionRange = useCallback(function () {
241
+ var editor = editorRef.current;
242
+ var selection = window.getSelection();
243
+ if (!editor || !selection || selection.rangeCount === 0) return;
244
+ var range = selection.getRangeAt(0);
245
+ if (!editor.contains(range.startContainer) || !editor.contains(range.endContainer)) {
246
+ return;
247
+ }
248
+ savedRangeRef.current = range.cloneRange();
158
249
  }, []);
159
250
  var restoreCursorAfterInsert = useCallback(function (node) {
160
251
  var editor = editorRef.current;
@@ -162,52 +253,80 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
162
253
  editor.focus();
163
254
  setCursorAtTextEnd(node);
164
255
  }, [setCursorAtTextEnd]);
165
- var isTagNode = function isTagNode(node) {
166
- return node instanceof HTMLElement && node.hasAttribute(TAG_ATTR);
256
+ var isMentionNode = function isMentionNode(node) {
257
+ return node instanceof HTMLElement && node.hasAttribute(MENTION_ATTR);
167
258
  };
168
259
  var isOnlyCaretText = function isOnlyCaretText(text) {
169
260
  return normalizeText(text) === "";
170
261
  };
171
262
 
172
- // Backspace/Delete 需要跨过零宽锚点识别相邻标签,不能只看光标所在文本节点。
173
- var getTagBeforeRange = function getTagBeforeRange(range) {
263
+ // Backspace/Delete 需要跨过零宽锚点识别相邻 Mention,不能只看光标所在文本节点。
264
+ var getMentionBeforeRange = function getMentionBeforeRange(range) {
174
265
  var container = range.startContainer;
175
266
  if (container.nodeType === Node.TEXT_NODE) {
176
267
  var text = container.textContent || "";
177
268
  var beforeCursor = text.slice(0, range.startOffset);
178
- if (isOnlyCaretText(beforeCursor) && isTagNode(container.previousSibling)) {
269
+ if (isOnlyCaretText(beforeCursor) && isMentionNode(container.previousSibling)) {
179
270
  return container.previousSibling;
180
271
  }
181
272
  }
182
273
  if (container instanceof HTMLElement) {
183
274
  var previous = container.childNodes[range.startOffset - 1];
184
- if (isTagNode(previous)) return previous;
185
- if ((previous === null || previous === void 0 ? void 0 : previous.nodeType) === Node.TEXT_NODE && isOnlyCaretText(previous.textContent || "") && isTagNode(previous.previousSibling)) {
275
+ if (isMentionNode(previous)) return previous;
276
+ if ((previous === null || previous === void 0 ? void 0 : previous.nodeType) === Node.TEXT_NODE && isOnlyCaretText(previous.textContent || "") && isMentionNode(previous.previousSibling)) {
186
277
  return previous.previousSibling;
187
278
  }
188
279
  }
189
280
  return null;
190
281
  };
191
- var getTagAfterRange = function getTagAfterRange(range) {
282
+ var getMentionAfterRange = function getMentionAfterRange(range) {
192
283
  var container = range.startContainer;
193
284
  if (container.nodeType === Node.TEXT_NODE) {
194
285
  var text = container.textContent || "";
195
286
  var afterCursor = text.slice(range.startOffset);
196
- if (isOnlyCaretText(afterCursor) && isTagNode(container.nextSibling)) {
287
+ if (isOnlyCaretText(afterCursor) && isMentionNode(container.nextSibling)) {
197
288
  return container.nextSibling;
198
289
  }
199
290
  }
200
291
  if (container instanceof HTMLElement) {
201
292
  var next = container.childNodes[range.startOffset];
202
- if (isTagNode(next)) return next;
203
- if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE && isOnlyCaretText(next.textContent || "") && isTagNode(next.nextSibling)) {
293
+ if (isMentionNode(next)) return next;
294
+ if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE && isOnlyCaretText(next.textContent || "") && isMentionNode(next.nextSibling)) {
204
295
  return next.nextSibling;
205
296
  }
206
297
  }
207
298
  return null;
208
299
  };
300
+ var moveCursorAroundMention = useCallback(function (mention, direction) {
301
+ var editor = editorRef.current;
302
+ var selection = window.getSelection();
303
+ if (!editor || !selection || !editor.contains(mention)) return;
304
+ var range = document.createRange();
305
+ if (direction === "before") {
306
+ var previous = mention.previousSibling;
307
+ if ((previous === null || previous === void 0 ? void 0 : previous.nodeType) === Node.TEXT_NODE) {
308
+ var _previous$textContent;
309
+ range.setStart(previous, ((_previous$textContent = previous.textContent) === null || _previous$textContent === void 0 ? void 0 : _previous$textContent.length) || 0);
310
+ } else {
311
+ range.setStartBefore(mention);
312
+ }
313
+ } else {
314
+ var next = mention.nextSibling;
315
+ if ((next === null || next === void 0 ? void 0 : next.nodeType) === Node.TEXT_NODE) {
316
+ var _match;
317
+ var leadingCaretLength = ((_match = (next.textContent || "").match(/^\u200B+/)) === null || _match === void 0 ? void 0 : _match[0].length) || 0;
318
+ range.setStart(next, leadingCaretLength);
319
+ } else {
320
+ range.setStartAfter(mention);
321
+ }
322
+ }
323
+ range.collapse(true);
324
+ selection.removeAllRanges();
325
+ selection.addRange(range);
326
+ savedRangeRef.current = range.cloneRange();
327
+ }, []);
209
328
 
210
- // 读取光标在纯文本中的位置,供快捷键搜索词和 tag 插入位置复用。
329
+ // 读取光标在纯文本中的位置,供快捷键搜索词和 Mention 插入位置复用。
211
330
  var getOffsetInEditor = useCallback(function () {
212
331
  var editor = editorRef.current;
213
332
  var selection = window.getSelection();
@@ -224,7 +343,7 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
224
343
  found = true;
225
344
  return;
226
345
  }
227
- if (node instanceof HTMLElement && node.hasAttribute(TAG_ATTR)) return;
346
+ if (node instanceof HTMLElement && node.hasAttribute(MENTION_ATTR)) return;
228
347
  if (node.nodeType === Node.TEXT_NODE) {
229
348
  offset += getPlainLength(node.textContent || "");
230
349
  return;
@@ -244,8 +363,8 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
244
363
  return index;
245
364
  };
246
365
 
247
- // 删除快捷键触发片段,例如把 “@文档” 替换成一个结构化 tag,并返回删除位置对应的真实 DOM Range。
248
- // 注意:纯文本 offset 不包含 tag,标签前后可能都是 offset 0;插入 tag 必须依赖真实 Range,不能再反推 offset。
366
+ // 删除快捷键触发片段,例如把 “@文档” 替换成一个结构化 Mention,并返回删除位置对应的真实 DOM Range。
367
+ // 注意:纯文本 offset 不包含 Mention,Mention 前后可能都是 offset 0;插入 Mention 必须依赖真实 Range,不能再反推 offset。
249
368
  var replaceTextRange = useCallback(function (start, end) {
250
369
  var editor = editorRef.current;
251
370
  if (!editor) return null;
@@ -281,7 +400,7 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
281
400
  return insertRange;
282
401
  }, []);
283
402
 
284
- // 浏览器拆分 Range 后可能留下空文本节点;清理后才能保证后续 tag 继续 append 在队尾。
403
+ // 浏览器拆分 Range 后可能留下空文本节点;清理后才能保证后续 Mention 继续 append 在队尾。
285
404
  var cleanupCaretTextNodes = useCallback(function () {
286
405
  var editor = editorRef.current;
287
406
  if (!editor) return;
@@ -297,43 +416,122 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
297
416
  }
298
417
  });
299
418
  }, []);
300
- var insertTag = useCallback(function (tag, replace) {
419
+
420
+ // Chrome 会把标签前临时输入的文字包进块级节点;文字被删空后,可能留下
421
+ // <div><br></div>。它没有消息语义,却会在 contenteditable 中占据一整行。
422
+ var cleanupEmptyStructuralNodes = useCallback(function () {
423
+ var editor = editorRef.current;
424
+ var selection = window.getSelection();
425
+ if (!editor) return;
426
+ Array.from(editor.children).forEach(function (node) {
427
+ if (node.hasAttribute(MENTION_ATTR)) return;
428
+ if (node.querySelector("[".concat(MENTION_ATTR, "]"))) return;
429
+ if (normalizeText(node.textContent || "")) return;
430
+ var range = selection !== null && selection !== void 0 && selection.rangeCount ? selection.getRangeAt(0) : null;
431
+ var nodeIndex = getNodeIndex(node);
432
+ var containsCursor = Boolean(range && range.collapsed && (range.startContainer === node || node.contains(range.startContainer) || range.startContainer === editor && (range.startOffset === nodeIndex || range.startOffset === nodeIndex + 1)));
433
+ if (containsCursor && selection) {
434
+ // 光标紧邻 contenteditable=false 的 Mention 时,单纯删除 <br> 会让 Chrome
435
+ // 再次生成它。替换成零宽文本锚点,既能承接光标,也不会增加行高。
436
+ var caretNode = createTextNode(CARET_TEXT);
437
+ node.replaceWith(caretNode);
438
+ var nextRange = document.createRange();
439
+ nextRange.setStart(caretNode, CARET_TEXT.length);
440
+ nextRange.collapse(true);
441
+ selection.removeAllRanges();
442
+ selection.addRange(nextRange);
443
+ savedRangeRef.current = nextRange.cloneRange();
444
+ return;
445
+ }
446
+ node.remove();
447
+ });
448
+ }, []);
449
+ useEffect(function () {
301
450
  var editor = editorRef.current;
302
451
  if (!editor) return;
452
+
453
+ // 某些业务容器会截断 React 委托事件;直接监听编辑器的原生 input,确保删除事务结束后
454
+ // 仍能清理 Chrome 在不可编辑 Mention 前生成的占行 <br>。仅处理 delete*,不影响 Enter 换行。
455
+ var handleNativeDelete = function handleNativeDelete(event) {
456
+ if (!(event instanceof InputEvent)) return;
457
+ if (!event.inputType.startsWith("delete")) return;
458
+ if (structuralCleanupFrameRef.current !== null) {
459
+ window.cancelAnimationFrame(structuralCleanupFrameRef.current);
460
+ }
461
+ structuralCleanupFrameRef.current = window.requestAnimationFrame(function () {
462
+ structuralCleanupFrameRef.current = null;
463
+ cleanupEmptyStructuralNodes();
464
+ cleanupCaretTextNodes();
465
+ });
466
+ };
467
+ editor.addEventListener("input", handleNativeDelete);
468
+ return function () {
469
+ return editor.removeEventListener("input", handleNativeDelete);
470
+ };
471
+ }, [cleanupCaretTextNodes, cleanupEmptyStructuralNodes]);
472
+ var insertMention = useCallback(function (mention, replace) {
473
+ var editor = editorRef.current;
474
+ if (!editor) return;
475
+ // 工具栏/弹窗会先抢走焦点;必须在 focus 之前保存当前 Selection,
476
+ // 否则浏览器新建在输入框开头的 Range 会覆盖最后一次真实光标位置。
477
+ var selectionBeforeFocus = window.getSelection();
478
+ var activeRangeBeforeFocus = selectionBeforeFocus !== null && selectionBeforeFocus !== void 0 && selectionBeforeFocus.rangeCount && editor.contains(selectionBeforeFocus.getRangeAt(0).startContainer) ? selectionBeforeFocus.getRangeAt(0).cloneRange() : null;
479
+ var savedRangeBeforeFocus = savedRangeRef.current && editor.contains(savedRangeRef.current.startContainer) ? savedRangeRef.current.cloneRange() : null;
480
+ var rangeBeforeFocus = activeRangeBeforeFocus || savedRangeBeforeFocus;
303
481
  editor.focus();
304
- latestTagsRef.current = [].concat(_toConsumableArray(latestTagsRef.current.filter(function (item) {
305
- return item.id !== tag.id;
306
- })), [tag]);
482
+ latestMentionsRef.current = [].concat(_toConsumableArray(latestMentionsRef.current.filter(function (item) {
483
+ return item.id !== mention.id;
484
+ })), [mention]);
307
485
  var insertRange = null;
308
486
  if (replace) {
309
487
  insertRange = replaceTextRange(replace.start, replace.end);
310
488
  cleanupCaretTextNodes();
311
489
  }
312
- var tagNode = buildTagNode(tag);
490
+ // 浏览器会在空 contenteditable 中生成 <div><br> 等占位块。
491
+ // 空编辑器插入首个 Mention 前先清掉这些无语义节点,避免标签被放进第二个视觉行。
492
+ var isSemanticallyEmpty = !getPlainText() && !editor.querySelector("[".concat(MENTION_ATTR, "]"));
493
+ if (isSemanticallyEmpty) {
494
+ editor.innerHTML = "";
495
+ savedRangeRef.current = null;
496
+ insertRange = null;
497
+ }
498
+ var mentionNode = buildMentionNode(mention);
313
499
  var caretNode = createTextNode(CARET_TEXT);
314
- var selection = window.getSelection();
315
- var range = insertRange || (selection !== null && selection !== void 0 && selection.rangeCount ? selection.getRangeAt(0) : null);
500
+ var range = insertRange || rangeBeforeFocus;
316
501
  if (range && editor.contains(range.startContainer)) {
317
- // 用 DocumentFragment 一次性插入,避免连续 insertNode 导致 tag/caret 顺序被浏览器 Range 状态打乱。
502
+ // 用 DocumentFragment 一次性插入,避免连续 insertNode 导致 mention/caret 顺序被浏览器 Range 状态打乱。
318
503
  var fragment = document.createDocumentFragment();
319
- fragment.append(tagNode, caretNode);
504
+ fragment.append(mentionNode, caretNode);
320
505
  range.insertNode(fragment);
321
506
  } else {
322
- editor.append(tagNode, caretNode);
507
+ editor.append(mentionNode, caretNode);
323
508
  }
324
509
  cleanupCaretTextNodes();
325
510
  lastInsertedCaretRef.current = caretNode;
326
511
  setCursorAtTextEnd(caretNode);
327
512
  emitChange();
328
513
 
329
- // 关闭快捷列表和父组件同步 tags/value 会触发一次 React 更新;
330
- // 下一帧再恢复到刚插入 tag 后的零宽锚点,避免光标被 focus 或重渲染带回旧位置。
514
+ // 关闭快捷列表和父组件同步 mentions/value 会触发一次 React 更新;
515
+ // 下一帧再恢复到刚插入 Mention 后的零宽锚点,避免光标被 focus 或重渲染带回旧位置。
331
516
  window.requestAnimationFrame(function () {
332
517
  if (lastInsertedCaretRef.current === caretNode) {
333
518
  restoreCursorAfterInsert(caretNode);
334
519
  }
335
520
  });
336
- }, [buildTagNode, cleanupCaretTextNodes, emitChange, replaceTextRange, restoreCursorAfterInsert, setCursorAtTextEnd]);
521
+ }, [buildMentionNode, cleanupCaretTextNodes, emitChange, getPlainText, replaceTextRange, restoreCursorAfterInsert, setCursorAtTextEnd]);
522
+ useEffect(function () {
523
+ document.addEventListener("selectionchange", saveSelectionRange);
524
+ return function () {
525
+ document.removeEventListener("selectionchange", saveSelectionRange);
526
+ };
527
+ }, [saveSelectionRange]);
528
+ useEffect(function () {
529
+ return function () {
530
+ if (structuralCleanupFrameRef.current !== null) {
531
+ window.cancelAnimationFrame(structuralCleanupFrameRef.current);
532
+ }
533
+ };
534
+ }, []);
337
535
  useImperativeHandle(ref, function () {
338
536
  return {
339
537
  focus: function focus() {
@@ -341,65 +539,150 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
341
539
  return (_editorRef$current = editorRef.current) === null || _editorRef$current === void 0 ? void 0 : _editorRef$current.focus();
342
540
  },
343
541
  getText: getPlainText,
542
+ getParts: getParts,
344
543
  getCursorOffset: getOffsetInEditor,
345
- insertTag: insertTag
544
+ insertMention: insertMention,
545
+ insertTag: insertMention
346
546
  };
347
- }, [getOffsetInEditor, getPlainText, insertTag]);
348
- useEffect(function () {
349
- var editor = editorRef.current;
350
- if (!editor || editor.childNodes.length > 0) return;
351
- if (value) editor.appendChild(createTextNode(value));
352
- tags.forEach(function (tag) {
353
- return editor.append(buildTagNode(tag), createTextNode(CARET_TEXT));
354
- });
355
- }, []);
356
-
357
- // 外部受控 tags 变化时,同步补齐或删除 DOM 中的 tag 节点。
358
- useEffect(function () {
547
+ }, [getOffsetInEditor, getParts, getPlainText, insertMention]);
548
+ useIsomorphicLayoutEffect(function () {
359
549
  var editor = editorRef.current;
360
550
  if (!editor) return;
361
- var existingNodes = Array.from(editor.querySelectorAll("[".concat(TAG_ATTR, "]")));
362
- var nextTagMap = new Map(tags.map(function (tag) {
363
- return [tag.id, tag];
364
- }));
365
- var existingIds = new Set(existingNodes.map(function (node) {
366
- return node.dataset.xadkInlineTagId || "";
367
- }));
368
- existingNodes.forEach(function (node) {
369
- var tagId = node.dataset.xadkInlineTagId || "";
370
- if (!nextTagMap.has(tagId)) {
371
- removeTagNode(node);
372
- }
373
- });
374
- tags.forEach(function (tag) {
375
- if (existingIds.has(tag.id)) return;
376
- editor.append(buildTagNode(tag), createTextNode(CARET_TEXT));
551
+
552
+ // 默认片段只在空输入首次挂载时灌入,避免外部重新渲染覆盖用户正在编辑的内容。
553
+ if (!defaultInputPartsAppliedRef.current && defaultInputParts !== null && defaultInputParts !== void 0 && defaultInputParts.length && !value && mentions.length === 0) {
554
+ defaultInputPartsAppliedRef.current = true;
555
+ // emitChange 的受控回写要到下一次 render 才生效;跳过本轮紧随其后的空值清理,
556
+ // 避免刚渲染的默认 Mention 被旧的空 value/mentions 立即移除。
557
+ skipEmptyCleanupOnceRef.current = true;
558
+ renderPartsToEditor(defaultInputParts);
559
+ emitChange();
560
+ return;
561
+ }
562
+ var incomingMentionIds = mentions.map(function (mention) {
563
+ return mention.id;
564
+ }).join("|");
565
+ var lastEmittedState = lastEmittedStateRef.current;
566
+ var isInternalControlledEcho = value === lastEmittedState.value && incomingMentionIds === lastEmittedState.mentionIds;
567
+ if (isInternalControlledEcho && editor.childNodes.length > 0) return;
568
+ renderPartsToEditor([].concat(_toConsumableArray(value ? [{
569
+ type: "text",
570
+ text: value
571
+ }] : []), _toConsumableArray(mentions.map(function (mention) {
572
+ return {
573
+ type: "mention",
574
+ mention: mention
575
+ };
576
+ }))));
577
+ setHasContent(Boolean(value || mentions.length));
578
+
579
+ // 外部主动写入 value/mentions 后,需要在首帧绘制前同步 DOM;若输入区已有焦点,则把光标放在内容末尾。
580
+ var lastTextNode = Array.from(editor.childNodes).reverse().find(function (node) {
581
+ return node.nodeType === Node.TEXT_NODE;
377
582
  });
378
- }, [buildTagNode, removeTagNode, tags]);
379
- useEffect(function () {
583
+ if (document.activeElement === editor && lastTextNode) {
584
+ setCursorAtTextEnd(lastTextNode);
585
+ }
586
+ }, [defaultInputParts, emitChange, renderPartsToEditor, setCursorAtTextEnd, mentions, value]);
587
+ useIsomorphicLayoutEffect(function () {
380
588
  var editor = editorRef.current;
381
589
  if (!editor) return;
382
- if (!value && tags.length === 0 && editor.childNodes.length > 0) {
383
- editor.querySelectorAll("[".concat(TAG_ATTR, "]")).forEach(function (node) {
384
- var _tagRootMapRef$curren2;
385
- (_tagRootMapRef$curren2 = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren2 === void 0 || _tagRootMapRef$curren2.unmount();
590
+ if (skipEmptyCleanupOnceRef.current) {
591
+ skipEmptyCleanupOnceRef.current = false;
592
+ return;
593
+ }
594
+ if (!value && mentions.length === 0 && editor.childNodes.length > 0) {
595
+ editor.querySelectorAll("[".concat(MENTION_ATTR, "]")).forEach(function (node) {
596
+ var _tagRootMapRef$curren3;
597
+ (_tagRootMapRef$curren3 = tagRootMapRef.current.get(node)) === null || _tagRootMapRef$curren3 === void 0 || _tagRootMapRef$curren3.unmount();
386
598
  });
387
599
  editor.innerHTML = "";
388
600
  }
389
- }, [tags.length, value]);
601
+ }, [mentions.length, value]);
390
602
  var handleInput = function handleInput() {
391
- if (!composingRef.current) emitChange();
603
+ if (!composingRef.current) {
604
+ cleanupEmptyStructuralNodes();
605
+ cleanupCaretTextNodes();
606
+ emitChange();
607
+
608
+ // Chrome 可能在 input 回调返回后才补上用于承接光标的 <br>;下一帧再归一化一次。
609
+ if (structuralCleanupFrameRef.current !== null) {
610
+ window.cancelAnimationFrame(structuralCleanupFrameRef.current);
611
+ }
612
+ structuralCleanupFrameRef.current = window.requestAnimationFrame(function () {
613
+ structuralCleanupFrameRef.current = null;
614
+ cleanupEmptyStructuralNodes();
615
+ cleanupCaretTextNodes();
616
+ });
617
+ }
618
+ };
619
+ var handlePaste = function handlePaste(event) {
620
+ event.preventDefault();
621
+ var editor = editorRef.current;
622
+ if (!editor) return;
623
+
624
+ // 始终按 text/plain 粘贴,避免消息气泡的背景色等 HTML 样式进入 contenteditable。
625
+ var text = event.clipboardData.getData("text/plain");
626
+ if (!text) return;
627
+ var parts = parseMarkdownResourceLinksAsParts(text);
628
+ var selection = window.getSelection();
629
+ var range = selection !== null && selection !== void 0 && selection.rangeCount && editor.contains(selection.getRangeAt(0).startContainer) ? selection.getRangeAt(0) : null;
630
+ var fragment = document.createDocumentFragment();
631
+ var lastTextNode = null;
632
+ var _iterator = _createForOfIteratorHelper(parts),
633
+ _step;
634
+ try {
635
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
636
+ var part = _step.value;
637
+ if (part.type === "text") {
638
+ lastTextNode = createTextNode(part.text);
639
+ fragment.append(lastTextNode);
640
+ continue;
641
+ }
642
+ latestMentionsRef.current = [].concat(_toConsumableArray(latestMentionsRef.current), [part.mention]);
643
+ fragment.append(buildMentionNode(part.mention));
644
+ lastTextNode = createTextNode(CARET_TEXT);
645
+ fragment.append(lastTextNode);
646
+ }
647
+ } catch (err) {
648
+ _iterator.e(err);
649
+ } finally {
650
+ _iterator.f();
651
+ }
652
+ if (range) {
653
+ range.deleteContents();
654
+ range.insertNode(fragment);
655
+ } else {
656
+ editor.append(fragment);
657
+ }
658
+ cleanupCaretTextNodes();
659
+ if (lastTextNode) setCursorAtTextEnd(lastTextNode);
660
+ emitChange();
392
661
  };
393
662
  var handleKeyDown = function handleKeyDown(event) {
394
- if (event.key === "Backspace" || event.key === "Delete") {
663
+ if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
395
664
  var selection = window.getSelection();
396
665
  if (selection !== null && selection !== void 0 && selection.rangeCount) {
397
666
  var range = selection.getRangeAt(0);
398
667
  if (range.collapsed) {
399
- var targetTag = event.key === "Backspace" ? getTagBeforeRange(range) : getTagAfterRange(range);
400
- if (targetTag) {
668
+ var targetMention = event.key === "ArrowLeft" ? getMentionBeforeRange(range) : getMentionAfterRange(range);
669
+ if (targetMention) {
401
670
  event.preventDefault();
402
- removeTagById(targetTag.dataset.xadkInlineTagId || "");
671
+ moveCursorAroundMention(targetMention, event.key === "ArrowLeft" ? "before" : "after");
672
+ return;
673
+ }
674
+ }
675
+ }
676
+ }
677
+ if (event.key === "Backspace" || event.key === "Delete") {
678
+ var _selection = window.getSelection();
679
+ if (_selection !== null && _selection !== void 0 && _selection.rangeCount) {
680
+ var _range = _selection.getRangeAt(0);
681
+ if (_range.collapsed) {
682
+ var _targetMention = event.key === "Backspace" ? getMentionBeforeRange(_range) : getMentionAfterRange(_range);
683
+ if (_targetMention) {
684
+ event.preventDefault();
685
+ removeMentionById(_targetMention.dataset.xadkInlineMentionId || "");
403
686
  return;
404
687
  }
405
688
  }
@@ -407,10 +690,15 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
407
690
  }
408
691
  onKeyDown === null || onKeyDown === void 0 || onKeyDown(event);
409
692
  };
410
- return /*#__PURE__*/_jsx("div", {
693
+
694
+ // placeholder 只由受控 value/mentions 派生,避免从 contenteditable DOM 读取导致外部插入 Mention 后状态滞后一帧。
695
+ var showPlaceholder = !hasContent;
696
+ return /*#__PURE__*/_jsxs("div", {
411
697
  className: styles.inlineInputWrapper,
412
- "data-placeholder": placeholder,
413
- children: /*#__PURE__*/_jsx("div", {
698
+ children: [showPlaceholder && placeholder && /*#__PURE__*/_jsx("div", {
699
+ className: styles.inlinePlaceholder,
700
+ children: placeholder
701
+ }), /*#__PURE__*/_jsx("div", {
414
702
  ref: editorRef,
415
703
  className: styles.inlineInput,
416
704
  contentEditable: !disabled,
@@ -418,18 +706,27 @@ var InlineTagInput = /*#__PURE__*/forwardRef(function (_ref, ref) {
418
706
  role: "textbox",
419
707
  "aria-multiline": "true",
420
708
  onInput: handleInput,
709
+ onPaste: handlePaste,
421
710
  onKeyDown: handleKeyDown,
422
- onKeyUp: onKeyUp,
711
+ onKeyUp: function onKeyUp(event) {
712
+ cleanupEmptyStructuralNodes();
713
+ cleanupCaretTextNodes();
714
+ saveSelectionRange();
715
+ _onKeyUp === null || _onKeyUp === void 0 || _onKeyUp(event);
716
+ },
717
+ onMouseUp: saveSelectionRange,
718
+ onFocus: saveSelectionRange,
423
719
  onCompositionStart: function onCompositionStart() {
424
720
  composingRef.current = true;
721
+ // 中文 IME 在 compositionend 前不会触发受控 value 更新;候选输入一出现就应隐藏 placeholder。
722
+ setHasContent(true);
425
723
  },
426
724
  onCompositionEnd: function onCompositionEnd() {
427
725
  composingRef.current = false;
428
726
  emitChange();
429
727
  },
430
- "data-empty": !getPlainText() && getOrderedTags().length === 0,
431
728
  "data-cursor-offset": getOffsetInEditor()
432
- })
729
+ })]
433
730
  });
434
731
  });
435
732
  InlineTagInput.displayName = "InlineTagInput";