@cloudscape-design/components 3.0.1270 → 3.0.1272

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 (181) hide show
  1. package/app-layout/visual-refresh-toolbar/toolbar/index.js +1 -1
  2. package/app-layout/visual-refresh-toolbar/toolbar/index.js.map +1 -1
  3. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.d.ts +1 -1
  4. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.d.ts.map +1 -1
  5. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.js +1 -2
  6. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/index.js.map +1 -1
  7. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.css.js +6 -7
  8. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.scoped.css +20 -26
  9. package/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.selectors.js +6 -7
  10. package/button/styles.css.js +22 -22
  11. package/button/styles.scoped.css +78 -78
  12. package/button/styles.selectors.js +22 -22
  13. package/help-panel/styles.css.js +6 -6
  14. package/help-panel/styles.scoped.css +70 -70
  15. package/help-panel/styles.selectors.js +6 -6
  16. package/i18n/messages/all.all.js +1 -1
  17. package/i18n/messages/all.all.json +1 -1
  18. package/i18n/messages/all.ar.js +1 -1
  19. package/i18n/messages/all.ar.json +1 -1
  20. package/i18n/messages/all.de.js +1 -1
  21. package/i18n/messages/all.de.json +1 -1
  22. package/i18n/messages/all.en-GB.js +1 -1
  23. package/i18n/messages/all.en-GB.json +1 -1
  24. package/i18n/messages/all.en.js +1 -1
  25. package/i18n/messages/all.en.json +1 -1
  26. package/i18n/messages/all.es.js +1 -1
  27. package/i18n/messages/all.es.json +1 -1
  28. package/i18n/messages/all.fr.js +1 -1
  29. package/i18n/messages/all.fr.json +1 -1
  30. package/i18n/messages/all.id.js +1 -1
  31. package/i18n/messages/all.id.json +1 -1
  32. package/i18n/messages/all.it.js +1 -1
  33. package/i18n/messages/all.it.json +1 -1
  34. package/i18n/messages/all.ja.js +1 -1
  35. package/i18n/messages/all.ja.json +1 -1
  36. package/i18n/messages/all.ko.js +1 -1
  37. package/i18n/messages/all.ko.json +1 -1
  38. package/i18n/messages/all.pt-BR.js +1 -1
  39. package/i18n/messages/all.pt-BR.json +1 -1
  40. package/i18n/messages/all.tr.js +1 -1
  41. package/i18n/messages/all.tr.json +1 -1
  42. package/i18n/messages/all.zh-CN.js +1 -1
  43. package/i18n/messages/all.zh-CN.json +1 -1
  44. package/i18n/messages/all.zh-TW.js +1 -1
  45. package/i18n/messages/all.zh-TW.json +1 -1
  46. package/i18n/messages-types.d.ts +18 -0
  47. package/i18n/messages-types.d.ts.map +1 -1
  48. package/i18n/messages-types.js.map +1 -1
  49. package/internal/base-component/styles.scoped.css +63 -4
  50. package/internal/components/token-list/styles.css.js +10 -10
  51. package/internal/components/token-list/styles.scoped.css +25 -25
  52. package/internal/components/token-list/styles.selectors.js +10 -10
  53. package/internal/environment.js +2 -2
  54. package/internal/environment.json +2 -2
  55. package/internal/generated/styles/tokens.d.ts +17 -3
  56. package/internal/generated/styles/tokens.js +17 -3
  57. package/internal/generated/theming/index.cjs +417 -9
  58. package/internal/generated/theming/index.cjs.d.ts +189 -0
  59. package/internal/generated/theming/index.d.ts +189 -0
  60. package/internal/generated/theming/index.js +417 -9
  61. package/internal/keycode.d.ts +3 -1
  62. package/internal/keycode.d.ts.map +1 -1
  63. package/internal/keycode.js +5 -0
  64. package/internal/keycode.js.map +1 -1
  65. package/internal/manifest.json +1 -1
  66. package/internal/utils/handle-key.d.ts +22 -1
  67. package/internal/utils/handle-key.d.ts.map +1 -1
  68. package/internal/utils/handle-key.js +62 -4
  69. package/internal/utils/handle-key.js.map +1 -1
  70. package/item-card/styles.css.js +14 -14
  71. package/item-card/styles.scoped.css +52 -52
  72. package/item-card/styles.selectors.js +14 -14
  73. package/link/styles.css.js +21 -21
  74. package/link/styles.scoped.css +81 -81
  75. package/link/styles.selectors.js +21 -21
  76. package/package.json +1 -1
  77. package/prompt-input/components/menu-dropdown.d.ts +33 -0
  78. package/prompt-input/components/menu-dropdown.d.ts.map +1 -0
  79. package/prompt-input/components/menu-dropdown.js +48 -0
  80. package/prompt-input/components/menu-dropdown.js.map +1 -0
  81. package/prompt-input/components/textarea-mode.d.ts +15 -0
  82. package/prompt-input/components/textarea-mode.d.ts.map +1 -0
  83. package/prompt-input/components/textarea-mode.js +8 -0
  84. package/prompt-input/components/textarea-mode.js.map +1 -0
  85. package/prompt-input/components/token-mode.d.ts +61 -0
  86. package/prompt-input/components/token-mode.d.ts.map +1 -0
  87. package/prompt-input/components/token-mode.js +37 -0
  88. package/prompt-input/components/token-mode.js.map +1 -0
  89. package/prompt-input/core/caret-controller.d.ts +73 -0
  90. package/prompt-input/core/caret-controller.d.ts.map +1 -0
  91. package/prompt-input/core/caret-controller.js +396 -0
  92. package/prompt-input/core/caret-controller.js.map +1 -0
  93. package/prompt-input/core/caret-spot-utils.d.ts +6 -0
  94. package/prompt-input/core/caret-spot-utils.d.ts.map +1 -0
  95. package/prompt-input/core/caret-spot-utils.js +52 -0
  96. package/prompt-input/core/caret-spot-utils.js.map +1 -0
  97. package/prompt-input/core/caret-utils.d.ts +25 -0
  98. package/prompt-input/core/caret-utils.d.ts.map +1 -0
  99. package/prompt-input/core/caret-utils.js +183 -0
  100. package/prompt-input/core/caret-utils.js.map +1 -0
  101. package/prompt-input/core/constants.d.ts +14 -0
  102. package/prompt-input/core/constants.d.ts.map +1 -0
  103. package/prompt-input/core/constants.js +18 -0
  104. package/prompt-input/core/constants.js.map +1 -0
  105. package/prompt-input/core/dom-utils.d.ts +60 -0
  106. package/prompt-input/core/dom-utils.d.ts.map +1 -0
  107. package/prompt-input/core/dom-utils.js +252 -0
  108. package/prompt-input/core/dom-utils.js.map +1 -0
  109. package/prompt-input/core/event-handlers.d.ts +68 -0
  110. package/prompt-input/core/event-handlers.d.ts.map +1 -0
  111. package/prompt-input/core/event-handlers.js +678 -0
  112. package/prompt-input/core/event-handlers.js.map +1 -0
  113. package/prompt-input/core/menu-state.d.ts +62 -0
  114. package/prompt-input/core/menu-state.d.ts.map +1 -0
  115. package/prompt-input/core/menu-state.js +168 -0
  116. package/prompt-input/core/menu-state.js.map +1 -0
  117. package/prompt-input/core/token-operations.d.ts +21 -0
  118. package/prompt-input/core/token-operations.d.ts.map +1 -0
  119. package/prompt-input/core/token-operations.js +273 -0
  120. package/prompt-input/core/token-operations.js.map +1 -0
  121. package/prompt-input/core/token-renderer.d.ts +26 -0
  122. package/prompt-input/core/token-renderer.d.ts.map +1 -0
  123. package/prompt-input/core/token-renderer.js +230 -0
  124. package/prompt-input/core/token-renderer.js.map +1 -0
  125. package/prompt-input/core/token-utils.d.ts +22 -0
  126. package/prompt-input/core/token-utils.d.ts.map +1 -0
  127. package/prompt-input/core/token-utils.js +262 -0
  128. package/prompt-input/core/token-utils.js.map +1 -0
  129. package/prompt-input/core/trigger-utils.d.ts +18 -0
  130. package/prompt-input/core/trigger-utils.d.ts.map +1 -0
  131. package/prompt-input/core/trigger-utils.js +174 -0
  132. package/prompt-input/core/trigger-utils.js.map +1 -0
  133. package/prompt-input/core/type-guards.d.ts +13 -0
  134. package/prompt-input/core/type-guards.d.ts.map +1 -0
  135. package/prompt-input/core/type-guards.js +36 -0
  136. package/prompt-input/core/type-guards.js.map +1 -0
  137. package/prompt-input/index.d.ts +1 -1
  138. package/prompt-input/index.d.ts.map +1 -1
  139. package/prompt-input/index.js.map +1 -1
  140. package/prompt-input/interfaces.d.ts +356 -7
  141. package/prompt-input/interfaces.d.ts.map +1 -1
  142. package/prompt-input/interfaces.js.map +1 -1
  143. package/prompt-input/internal.d.ts +1 -1
  144. package/prompt-input/internal.d.ts.map +1 -1
  145. package/prompt-input/internal.js +195 -61
  146. package/prompt-input/internal.js.map +1 -1
  147. package/prompt-input/styles.css.js +26 -17
  148. package/prompt-input/styles.scoped.css +152 -39
  149. package/prompt-input/styles.selectors.js +26 -17
  150. package/prompt-input/test-classes/styles.css.js +7 -6
  151. package/prompt-input/test-classes/styles.scoped.css +10 -6
  152. package/prompt-input/test-classes/styles.selectors.js +7 -6
  153. package/prompt-input/tokens/use-shortcuts.d.ts +37 -0
  154. package/prompt-input/tokens/use-shortcuts.d.ts.map +1 -0
  155. package/prompt-input/tokens/use-shortcuts.js +89 -0
  156. package/prompt-input/tokens/use-shortcuts.js.map +1 -0
  157. package/prompt-input/tokens/use-token-mode.d.ts +78 -0
  158. package/prompt-input/tokens/use-token-mode.d.ts.map +1 -0
  159. package/prompt-input/tokens/use-token-mode.js +817 -0
  160. package/prompt-input/tokens/use-token-mode.js.map +1 -0
  161. package/prompt-input/utils/insert-text-content-editable.d.ts +10 -0
  162. package/prompt-input/utils/insert-text-content-editable.d.ts.map +1 -0
  163. package/prompt-input/utils/insert-text-content-editable.js +133 -0
  164. package/prompt-input/utils/insert-text-content-editable.js.map +1 -0
  165. package/tabs/styles.css.js +30 -30
  166. package/tabs/styles.scoped.css +55 -55
  167. package/tabs/styles.selectors.js +30 -30
  168. package/test-utils/dom/hotspot/index.js +1 -2
  169. package/test-utils/dom/hotspot/index.js.map +1 -1
  170. package/test-utils/dom/prompt-input/index.d.ts +53 -1
  171. package/test-utils/dom/prompt-input/index.js +121 -13
  172. package/test-utils/dom/prompt-input/index.js.map +1 -1
  173. package/test-utils/dom/s3-resource-selector/index.js +2 -3
  174. package/test-utils/dom/s3-resource-selector/index.js.map +1 -1
  175. package/test-utils/selectors/hotspot/index.js +1 -2
  176. package/test-utils/selectors/hotspot/index.js.map +1 -1
  177. package/test-utils/selectors/prompt-input/index.d.ts +30 -0
  178. package/test-utils/selectors/prompt-input/index.js +53 -7
  179. package/test-utils/selectors/prompt-input/index.js.map +1 -1
  180. package/test-utils/selectors/s3-resource-selector/index.js +2 -3
  181. package/test-utils/selectors/s3-resource-selector/index.js.map +1 -1
@@ -0,0 +1,678 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { findUpUntil } from '@cloudscape-design/component-toolkit/dom';
4
+ import { fireKeyboardEvent } from '../../internal/events';
5
+ import { isHTMLElement } from '../../internal/utils/dom';
6
+ import handleKey from '../../internal/utils/handle-key';
7
+ import { getOwnerSelection, TOKEN_LENGTHS } from './caret-controller';
8
+ import { ElementType } from './constants';
9
+ import { createParagraph, createTrailingBreak, findAdjacentToken, findAllParagraphs, getTokenType, insertAfter, isCaretSpotType, isElementEffectivelyEmpty, isReferenceElementType, stripZeroWidthCharacters, } from './dom-utils';
10
+ import { getPromptText } from './token-operations';
11
+ import { removeTokenRange } from './token-utils';
12
+ import { handleDeleteAfterTrigger, handleSpaceInOpenMenu } from './trigger-utils';
13
+ import { isBreakTextToken, isBRElement, isTextNode } from './type-guards';
14
+ /** Handles all keyboard events for the editable element. */
15
+ export function handleEditableKeyDown(event, props) {
16
+ const { editableElement, editableState, caretController, tokens, tokensToText } = props;
17
+ const emitChange = (detail) => {
18
+ props.markTokensAsSent(detail.tokens);
19
+ props.onChange(detail);
20
+ };
21
+ const emitTokenDeletion = (newTokens, caretPos) => {
22
+ const value = tokensToText ? tokensToText(newTokens) : getPromptText(newTokens);
23
+ // Don't mark as sent — selection deletions use preventDefault so the DOM
24
+ // is NOT updated by the browser. The render effect must see these as an
25
+ // external change and re-render the DOM to match the new token state.
26
+ props.onChange({ value, tokens: newTokens });
27
+ if (caretController) {
28
+ caretController.setCapturedPosition(caretPos);
29
+ }
30
+ };
31
+ // Forward to consumer's onKeyDown
32
+ fireKeyboardEvent(props.onKeyDown, event);
33
+ const menuItemsState = props.getMenuItemsState();
34
+ const menuItemsHandlers = props.getMenuItemsHandlers();
35
+ const menuOpen = props.getMenuOpen();
36
+ handleKey(event, {
37
+ onInlineStart: () => handleInlineStart(event, caretController, props.announceTokenOperation),
38
+ onInlineEnd: () => handleInlineEnd(event, caretController, props.announceTokenOperation),
39
+ onShiftInlineStart: () => handleInlineStart(event, caretController, props.announceTokenOperation),
40
+ onShiftInlineEnd: () => handleInlineEnd(event, caretController, props.announceTokenOperation),
41
+ onSelectAll: () => {
42
+ if ((tokens === null || tokens === void 0 ? void 0 : tokens.length) === 0) {
43
+ event.preventDefault();
44
+ }
45
+ },
46
+ onBackspace: () => {
47
+ if (editableElement &&
48
+ handleReferenceTokenDeletion(event, true, editableElement, editableState, props.announceTokenOperation, props.i18nStrings, caretController, tokens, emitTokenDeletion)) {
49
+ return;
50
+ }
51
+ if (!tokens || !editableElement) {
52
+ return;
53
+ }
54
+ if (tokens.length === 0) {
55
+ event.preventDefault();
56
+ return;
57
+ }
58
+ handleBackspaceAtParagraphStart(event, editableElement, tokens, tokensToText, emitChange, caretController);
59
+ },
60
+ onDelete: () => {
61
+ if (editableElement &&
62
+ handleReferenceTokenDeletion(event, false, editableElement, editableState, props.announceTokenOperation, props.i18nStrings, caretController, tokens, emitTokenDeletion)) {
63
+ return;
64
+ }
65
+ if (!tokens || !editableElement) {
66
+ return;
67
+ }
68
+ if (handleDeleteAtParagraphEnd(event, editableElement, tokens, tokensToText, emitChange, caretController)) {
69
+ return;
70
+ }
71
+ handleDeleteAfterTrigger(event, editableElement);
72
+ },
73
+ onShiftEnter: () => {
74
+ if (event.nativeEvent.isComposing) {
75
+ return;
76
+ }
77
+ event.preventDefault();
78
+ if (caretController === null || caretController === void 0 ? void 0 : caretController.findActiveTrigger()) {
79
+ return;
80
+ }
81
+ if (editableElement) {
82
+ splitParagraphAtCaret(editableElement, caretController);
83
+ }
84
+ },
85
+ onEnter: () => {
86
+ if (event.nativeEvent.isComposing) {
87
+ return;
88
+ }
89
+ if (menuOpen && menuItemsHandlers) {
90
+ event.preventDefault();
91
+ menuItemsHandlers.selectHighlightedOptionWithKeyboard();
92
+ return;
93
+ }
94
+ handleEnterSubmit(event, props);
95
+ },
96
+ onTab: () => {
97
+ if (menuOpen && menuItemsHandlers && !event.shiftKey) {
98
+ event.preventDefault();
99
+ menuItemsHandlers.selectHighlightedOptionWithKeyboard();
100
+ }
101
+ },
102
+ onSpace: () => {
103
+ if (editableElement && handleSpaceAfterClosedTrigger(event, editableElement, menuOpen, caretController)) {
104
+ return;
105
+ }
106
+ if (menuOpen && menuItemsHandlers && menuItemsState) {
107
+ handleSpaceInOpenMenu(event, {
108
+ menuItemsState,
109
+ menuItemsHandlers,
110
+ getMenuStatusType: props.getMenuStatusType,
111
+ closeMenu: props.closeMenu,
112
+ caretController: caretController !== null && caretController !== void 0 ? caretController : undefined,
113
+ editableElement: editableElement !== null && editableElement !== void 0 ? editableElement : undefined,
114
+ });
115
+ }
116
+ },
117
+ onBlockEnd: () => {
118
+ if (menuOpen && menuItemsHandlers) {
119
+ event.preventDefault();
120
+ menuItemsHandlers.moveHighlightWithKeyboard(1);
121
+ }
122
+ },
123
+ onBlockStart: () => {
124
+ if (menuOpen && menuItemsHandlers) {
125
+ event.preventDefault();
126
+ menuItemsHandlers.moveHighlightWithKeyboard(-1);
127
+ }
128
+ },
129
+ onEscape: () => {
130
+ if (menuOpen) {
131
+ event.preventDefault();
132
+ props.closeMenu();
133
+ }
134
+ },
135
+ });
136
+ }
137
+ /** Handles Enter key for form submission and onAction. */
138
+ function handleEnterSubmit(event, props) {
139
+ var _a, _b, _c;
140
+ if (props.disabled || props.readOnly) {
141
+ event.preventDefault();
142
+ return;
143
+ }
144
+ const currentTarget = event.currentTarget;
145
+ if (!isHTMLElement(currentTarget)) {
146
+ return;
147
+ }
148
+ const form = currentTarget.closest('form');
149
+ if (form && !event.isDefaultPrevented()) {
150
+ form.requestSubmit();
151
+ }
152
+ event.preventDefault();
153
+ const plainText = props.tokensToText ? props.tokensToText((_a = props.tokens) !== null && _a !== void 0 ? _a : []) : getPromptText((_b = props.tokens) !== null && _b !== void 0 ? _b : []);
154
+ if (props.onAction) {
155
+ props.onAction({ value: plainText, tokens: [...((_c = props.tokens) !== null && _c !== void 0 ? _c : [])] });
156
+ }
157
+ }
158
+ /** Splits the current paragraph at the caret position, creating a new paragraph below. */
159
+ export function splitParagraphAtCaret(editableElement, caretController, suppressInputEvent = false) {
160
+ var _a;
161
+ const selection = getOwnerSelection(editableElement);
162
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
163
+ return;
164
+ }
165
+ const range = selection.getRangeAt(0);
166
+ const startElement = isHTMLElement(range.startContainer) ? range.startContainer : range.startContainer.parentElement;
167
+ const currentP = startElement ? findUpUntil(startElement, node => node.nodeName === 'P') : null;
168
+ if (!(currentP === null || currentP === void 0 ? void 0 : currentP.parentNode)) {
169
+ return;
170
+ }
171
+ const afterRange = ((_a = editableElement.ownerDocument) !== null && _a !== void 0 ? _a : document).createRange();
172
+ afterRange.setStart(range.startContainer, range.startOffset);
173
+ afterRange.setEndAfter(currentP.lastChild || currentP);
174
+ const afterContent = afterRange.extractContents();
175
+ // Strip trailing breaks from extracted content — they belong only in empty paragraphs
176
+ for (const child of Array.from(afterContent.childNodes)) {
177
+ if (child.nodeName === 'BR') {
178
+ child.remove();
179
+ }
180
+ }
181
+ const newP = createParagraph();
182
+ newP.appendChild(afterContent);
183
+ if (isElementEffectivelyEmpty(newP)) {
184
+ newP.appendChild(createTrailingBreak());
185
+ }
186
+ if (isElementEffectivelyEmpty(currentP)) {
187
+ currentP.appendChild(createTrailingBreak());
188
+ }
189
+ currentP.parentNode.insertBefore(newP, currentP.nextSibling);
190
+ let newCaretPos = null;
191
+ if (caretController) {
192
+ const currentPos = caretController.getPosition();
193
+ newCaretPos = currentPos + TOKEN_LENGTHS.LINE_BREAK;
194
+ }
195
+ if (!suppressInputEvent) {
196
+ editableElement.dispatchEvent(new Event('input', { bubbles: true }));
197
+ }
198
+ if (caretController && newCaretPos !== null) {
199
+ caretController.setPosition(newCaretPos);
200
+ }
201
+ }
202
+ function findTokenElementForDeletion(container, offset, isBackspace) {
203
+ var _a;
204
+ let adjacent = null;
205
+ if (isTextNode(container)) {
206
+ const isAtEdge = isBackspace ? offset === 0 : offset === (((_a = container.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0);
207
+ if (isAtEdge) {
208
+ adjacent = isBackspace ? container.previousSibling : container.nextSibling;
209
+ }
210
+ }
211
+ else if (isHTMLElement(container)) {
212
+ const childIndex = isBackspace ? offset - 1 : offset;
213
+ adjacent = container.childNodes[childIndex];
214
+ }
215
+ if (isHTMLElement(adjacent)) {
216
+ const adjacentType = getTokenType(adjacent);
217
+ if (isReferenceElementType(adjacentType)) {
218
+ return {
219
+ wrapperElement: adjacent,
220
+ targetElement: adjacent,
221
+ };
222
+ }
223
+ }
224
+ return { targetElement: null, wrapperElement: null };
225
+ }
226
+ function isValidTokenForDeletion(element) {
227
+ if (!element) {
228
+ return false;
229
+ }
230
+ const tokenType = getTokenType(element);
231
+ return isReferenceElementType(tokenType);
232
+ }
233
+ /** Handles Backspace/Delete when adjacent to a reference token. Returns true if handled. */
234
+ export function handleReferenceTokenDeletion(event, isBackspace, editableElement, state, announceTokenOperation, i18nStrings, caretController, tokens, emitChange) {
235
+ var _a, _b;
236
+ const selection = getOwnerSelection(editableElement);
237
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
238
+ return false;
239
+ }
240
+ const range = selection.getRangeAt(0);
241
+ if (!range.collapsed) {
242
+ event.preventDefault();
243
+ if (!caretController || !tokens || !emitChange) {
244
+ return false;
245
+ }
246
+ // Map the DOM selection to logical token positions and remove the range
247
+ // from state. The render effect will update the DOM on the next cycle.
248
+ const startPos = caretController.getPosition();
249
+ // Get end position by temporarily collapsing the range to its end
250
+ const tempRange = range.cloneRange();
251
+ tempRange.collapse(false);
252
+ selection.removeAllRanges();
253
+ selection.addRange(tempRange);
254
+ const endPos = caretController.getPosition();
255
+ selection.removeAllRanges();
256
+ selection.addRange(range);
257
+ const rangeStart = Math.min(startPos, endPos);
258
+ const rangeEnd = Math.max(startPos, endPos);
259
+ const newTokens = removeTokenRange(tokens, rangeStart, rangeEnd);
260
+ emitChange(newTokens, rangeStart);
261
+ return true;
262
+ }
263
+ const { targetElement, wrapperElement } = findTokenElementForDeletion(range.startContainer, range.startOffset, isBackspace);
264
+ const tokenElement = targetElement || wrapperElement || null;
265
+ if (!isValidTokenForDeletion(tokenElement)) {
266
+ return false;
267
+ }
268
+ const elementToRemove = (wrapperElement || tokenElement);
269
+ const paragraph = elementToRemove.parentNode;
270
+ if (!paragraph) {
271
+ return false;
272
+ }
273
+ event.preventDefault();
274
+ const tokenLabel = ((_a = tokenElement.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '';
275
+ if (announceTokenOperation && tokenLabel) {
276
+ const announcement = (_b = i18nStrings === null || i18nStrings === void 0 ? void 0 : i18nStrings.tokenRemovedAriaLabel) === null || _b === void 0 ? void 0 : _b.call(i18nStrings, { label: tokenLabel, value: tokenLabel });
277
+ if (announcement) {
278
+ announceTokenOperation(announcement);
279
+ }
280
+ }
281
+ state.skipNextZeroWidthUpdate = true;
282
+ let newCaretPos = null;
283
+ if (caretController) {
284
+ const currentPos = caretController.getPosition();
285
+ newCaretPos = isBackspace ? Math.max(0, currentPos - TOKEN_LENGTHS.REFERENCE) : currentPos;
286
+ }
287
+ elementToRemove.remove();
288
+ editableElement.dispatchEvent(new Event('input', { bubbles: true }));
289
+ if (caretController && newCaretPos !== null) {
290
+ caretController.setPosition(newCaretPos);
291
+ }
292
+ return true;
293
+ }
294
+ /** If the caret is inside a reference's caret-spot, normalizes it to the paragraph level. */
295
+ function normalizeCaretOutOfReference(container, direction, event, selection) {
296
+ var _a;
297
+ if (!isTextNode(container)) {
298
+ return false;
299
+ }
300
+ const parent = container.parentElement;
301
+ if (!parent || !isCaretSpotType(getTokenType(parent))) {
302
+ return false;
303
+ }
304
+ const wrapper = parent.parentElement;
305
+ if (!wrapper || !isReferenceElementType(getTokenType(wrapper))) {
306
+ return false;
307
+ }
308
+ const paragraph = wrapper.parentElement;
309
+ if (!paragraph) {
310
+ return false;
311
+ }
312
+ const wrapperIndex = Array.from(paragraph.childNodes).indexOf(wrapper);
313
+ const newOffset = direction === 'backward' ? wrapperIndex : wrapperIndex + 1;
314
+ event.preventDefault();
315
+ const newRange = ((_a = paragraph.ownerDocument) !== null && _a !== void 0 ? _a : document).createRange();
316
+ newRange.setStart(paragraph, newOffset);
317
+ newRange.collapse(true);
318
+ selection.removeAllRanges();
319
+ selection.addRange(newRange);
320
+ return true;
321
+ }
322
+ /** Handles inline-start (backward) arrow key — plain navigation or shift+selection across references. */
323
+ export function handleInlineStart(event, caretController, announceTokenOperation) {
324
+ var _a;
325
+ const selection = getOwnerSelection(event.currentTarget);
326
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
327
+ return;
328
+ }
329
+ const range = selection.getRangeAt(0);
330
+ if (range.collapsed && normalizeCaretOutOfReference(range.startContainer, 'backward', event, selection)) {
331
+ return;
332
+ }
333
+ if (event.shiftKey) {
334
+ handleShiftArrowAcrossTokens(event, selection, range, 'backward');
335
+ return;
336
+ }
337
+ const { sibling, isReferenceToken } = findAdjacentToken(range.startContainer, range.startOffset, 'backward');
338
+ if (isReferenceToken && sibling) {
339
+ event.preventDefault();
340
+ caretController === null || caretController === void 0 ? void 0 : caretController.moveBackward(TOKEN_LENGTHS.REFERENCE);
341
+ if (announceTokenOperation && isHTMLElement(sibling)) {
342
+ const label = stripZeroWidthCharacters(((_a = sibling.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '');
343
+ if (label) {
344
+ announceTokenOperation(label);
345
+ }
346
+ }
347
+ }
348
+ }
349
+ /** Handles inline-end (forward) arrow key — plain navigation or shift+selection across references. */
350
+ export function handleInlineEnd(event, caretController, announceTokenOperation) {
351
+ var _a;
352
+ const selection = getOwnerSelection(event.currentTarget);
353
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
354
+ return;
355
+ }
356
+ const range = selection.getRangeAt(0);
357
+ if (range.collapsed && normalizeCaretOutOfReference(range.startContainer, 'forward', event, selection)) {
358
+ return;
359
+ }
360
+ if (event.shiftKey) {
361
+ handleShiftArrowAcrossTokens(event, selection, range, 'forward');
362
+ return;
363
+ }
364
+ const { sibling, isReferenceToken } = findAdjacentToken(range.startContainer, range.startOffset, 'forward');
365
+ if (isReferenceToken && sibling) {
366
+ event.preventDefault();
367
+ caretController === null || caretController === void 0 ? void 0 : caretController.moveForward(TOKEN_LENGTHS.REFERENCE);
368
+ if (announceTokenOperation && isHTMLElement(sibling)) {
369
+ const label = stripZeroWidthCharacters(((_a = sibling.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || '');
370
+ if (label) {
371
+ announceTokenOperation(label);
372
+ }
373
+ }
374
+ }
375
+ }
376
+ /** After the browser handles Shift+Arrow, nudge the focus past any reference it landed inside. */
377
+ function handleShiftArrowAcrossTokens(event, selection, range, direction) {
378
+ var _a, _b;
379
+ const isBackward = direction === 'backward';
380
+ // Check if the focus is adjacent to a reference in the arrow direction
381
+ const focusNode = selection.focusNode;
382
+ const focusOff = selection.focusOffset;
383
+ if (!focusNode) {
384
+ return false;
385
+ }
386
+ let adjacentRef = null;
387
+ // If focus is inside a reference (e.g. in a caret-spot), the reference itself is what we skip
388
+ let containingRef = null;
389
+ let node = isTextNode(focusNode) ? focusNode.parentElement : focusNode;
390
+ while (node && isHTMLElement(node) && node !== event.currentTarget) {
391
+ const tokenType = getTokenType(node);
392
+ if (isReferenceElementType(tokenType)) {
393
+ containingRef = node;
394
+ break;
395
+ }
396
+ if (isCaretSpotType(tokenType) && node.parentElement) {
397
+ const parentType = getTokenType(node.parentElement);
398
+ if (isReferenceElementType(parentType)) {
399
+ containingRef = node.parentElement;
400
+ break;
401
+ }
402
+ }
403
+ node = node.parentElement;
404
+ }
405
+ if (containingRef) {
406
+ adjacentRef = containingRef;
407
+ }
408
+ else if (isTextNode(focusNode)) {
409
+ if (isBackward && focusOff === 0) {
410
+ adjacentRef = focusNode.previousSibling;
411
+ }
412
+ else if (!isBackward) {
413
+ const len = ((_a = focusNode.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0;
414
+ // Check at the text boundary — only jump over the reference when focus
415
+ // is at the very end of the text node, not one character before.
416
+ if (focusOff >= len && focusNode.nextSibling) {
417
+ const nextSibling = focusNode.nextSibling;
418
+ if (isHTMLElement(nextSibling) && isReferenceElementType(getTokenType(nextSibling))) {
419
+ adjacentRef = nextSibling;
420
+ }
421
+ }
422
+ }
423
+ if (!adjacentRef && isBackward && focusOff === 0 && focusNode.previousSibling) {
424
+ const previousSibling = focusNode.previousSibling;
425
+ if (isHTMLElement(previousSibling) && isReferenceElementType(getTokenType(previousSibling))) {
426
+ adjacentRef = previousSibling;
427
+ }
428
+ }
429
+ }
430
+ else if (isHTMLElement(focusNode)) {
431
+ if (isBackward && focusOff > 0) {
432
+ adjacentRef = focusNode.childNodes[focusOff - 1];
433
+ }
434
+ else if (!isBackward && focusOff < focusNode.childNodes.length) {
435
+ adjacentRef = focusNode.childNodes[focusOff];
436
+ }
437
+ }
438
+ if (!adjacentRef || !isHTMLElement(adjacentRef) || !isReferenceElementType(getTokenType(adjacentRef))) {
439
+ return false;
440
+ }
441
+ const parent = adjacentRef.parentNode;
442
+ if (!parent) {
443
+ return false;
444
+ }
445
+ event.preventDefault();
446
+ const index = Array.from(parent.childNodes).indexOf(adjacentRef);
447
+ // Find the actual text node on the far side of the reference to extend into,
448
+ // rather than using paragraph-level offsets which the browser may normalize
449
+ // into the reference's caret-spot internals.
450
+ let targetNode = parent;
451
+ let targetOffset = isBackward ? index : index + 1;
452
+ if (!isBackward) {
453
+ const nextSibling = adjacentRef.nextSibling;
454
+ if (nextSibling && isTextNode(nextSibling)) {
455
+ targetNode = nextSibling;
456
+ targetOffset = 0;
457
+ }
458
+ }
459
+ else {
460
+ const prevSibling = adjacentRef.previousSibling;
461
+ if (prevSibling && isTextNode(prevSibling)) {
462
+ targetNode = prevSibling;
463
+ targetOffset = ((_b = prevSibling.textContent) === null || _b === void 0 ? void 0 : _b.length) || 0;
464
+ }
465
+ }
466
+ // If extending would jump the focus past the anchor (deselecting through a reference),
467
+ // collapse instead of flipping the selection direction.
468
+ if (!range.collapsed && selection.anchorNode) {
469
+ const anchorPos = adjacentRef.compareDocumentPosition(selection.anchorNode);
470
+ const anchorIsAfter = (anchorPos & Node.DOCUMENT_POSITION_FOLLOWING) !== 0 || (anchorPos & Node.DOCUMENT_POSITION_CONTAINED_BY) !== 0;
471
+ const anchorIsBefore = (anchorPos & Node.DOCUMENT_POSITION_PRECEDING) !== 0 || (anchorPos & Node.DOCUMENT_POSITION_CONTAINS) !== 0;
472
+ if ((!isBackward && anchorIsAfter) || (isBackward && anchorIsBefore)) {
473
+ const anchorNode = selection.anchorNode;
474
+ const anchorOffset = selection.anchorOffset;
475
+ selection.collapse(anchorNode, anchorOffset);
476
+ selection.extend(targetNode, targetOffset);
477
+ return true;
478
+ }
479
+ }
480
+ selection.extend(targetNode, targetOffset);
481
+ return true;
482
+ }
483
+ /** Handles space key after a closed trigger element, inserting the space outside the trigger. */
484
+ export function handleSpaceAfterClosedTrigger(event, editableElement, menuOpen, caretController) {
485
+ var _a, _b;
486
+ if (event.key !== ' ' || menuOpen) {
487
+ return false;
488
+ }
489
+ const selection = getOwnerSelection(editableElement);
490
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
491
+ return false;
492
+ }
493
+ const range = selection.getRangeAt(0);
494
+ if (!range.collapsed) {
495
+ return false;
496
+ }
497
+ let triggerElement = null;
498
+ let caretAtEnd = false;
499
+ if (isTextNode(range.startContainer)) {
500
+ const parent = range.startContainer.parentElement;
501
+ const parentType = parent ? getTokenType(parent) : null;
502
+ if (parentType === ElementType.Trigger && parent) {
503
+ triggerElement = parent;
504
+ const textLength = ((_a = range.startContainer.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0;
505
+ caretAtEnd = range.startOffset === textLength;
506
+ }
507
+ }
508
+ else if (isHTMLElement(range.startContainer)) {
509
+ const container = range.startContainer;
510
+ if (range.startOffset > 0) {
511
+ const prevNode = container.childNodes[range.startOffset - 1];
512
+ if (isHTMLElement(prevNode) && getTokenType(prevNode) === ElementType.Trigger) {
513
+ triggerElement = prevNode;
514
+ caretAtEnd = true;
515
+ }
516
+ }
517
+ }
518
+ if (!triggerElement || !caretAtEnd) {
519
+ return false;
520
+ }
521
+ const paragraph = triggerElement.parentElement;
522
+ if (!paragraph) {
523
+ return false;
524
+ }
525
+ event.preventDefault();
526
+ if (caretController) {
527
+ caretController.capture();
528
+ }
529
+ const spaceNode = ((_b = triggerElement.ownerDocument) !== null && _b !== void 0 ? _b : document).createTextNode(' ');
530
+ insertAfter(spaceNode, triggerElement);
531
+ editableElement.dispatchEvent(new Event('input', { bubbles: true }));
532
+ if (caretController) {
533
+ caretController.restore(1);
534
+ }
535
+ return true;
536
+ }
537
+ /** Merges two adjacent paragraphs by removing the break token between them. */
538
+ export function mergeParagraphs(params) {
539
+ const { direction, editableElement, tokens, currentParagraphIndex, tokensToText, onChange, caretController } = params;
540
+ const paragraphs = findAllParagraphs(editableElement);
541
+ if (direction === 'backward') {
542
+ if (currentParagraphIndex <= 0) {
543
+ return false;
544
+ }
545
+ }
546
+ else {
547
+ if (currentParagraphIndex >= paragraphs.length - 1) {
548
+ return false;
549
+ }
550
+ }
551
+ const breakIndexToRemove = direction === 'backward' ? currentParagraphIndex : currentParagraphIndex + 1;
552
+ let breakCount = 0;
553
+ let breakRemoved = false;
554
+ const newTokens = tokens.filter(token => {
555
+ if (isBreakTextToken(token)) {
556
+ breakCount++;
557
+ if (breakCount === breakIndexToRemove) {
558
+ breakRemoved = true;
559
+ return false;
560
+ }
561
+ }
562
+ return true;
563
+ });
564
+ if (!breakRemoved) {
565
+ return false;
566
+ }
567
+ const value = tokensToText ? tokensToText(newTokens) : getPromptText(newTokens);
568
+ onChange({ value, tokens: newTokens });
569
+ if (caretController) {
570
+ const currentPos = caretController.getPosition();
571
+ // Backspace: cursor was at start of next paragraph, now needs to move back by the removed break
572
+ // Delete: cursor stays where it is — the next line merges into the current one
573
+ const newCaretPos = direction === 'backward' ? currentPos - TOKEN_LENGTHS.LINE_BREAK : currentPos;
574
+ caretController.setPosition(newCaretPos);
575
+ }
576
+ return true;
577
+ }
578
+ /** Handles Backspace at the start of a paragraph by merging with the previous one. */
579
+ export function handleBackspaceAtParagraphStart(event, editableElement, tokens, tokensToText, onChange, caretController) {
580
+ const selection = getOwnerSelection(editableElement);
581
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
582
+ return false;
583
+ }
584
+ const range = selection.getRangeAt(0);
585
+ if (!range.collapsed ||
586
+ range.startOffset !== 0 ||
587
+ !isHTMLElement(range.startContainer) ||
588
+ range.startContainer.nodeName !== 'P') {
589
+ return false;
590
+ }
591
+ const paragraphs = findAllParagraphs(editableElement);
592
+ const currentP = range.startContainer;
593
+ const pIndex = Array.from(paragraphs).indexOf(currentP);
594
+ if (pIndex < 0) {
595
+ return false;
596
+ }
597
+ const merged = mergeParagraphs({
598
+ direction: 'backward',
599
+ editableElement,
600
+ tokens,
601
+ currentParagraphIndex: pIndex,
602
+ tokensToText,
603
+ onChange,
604
+ caretController: caretController,
605
+ });
606
+ if (merged) {
607
+ event.preventDefault();
608
+ }
609
+ return merged;
610
+ }
611
+ /** Handles Delete at the end of a paragraph by merging with the next one. */
612
+ export function handleDeleteAtParagraphEnd(event, editableElement, tokens, tokensToText, onChange, caretController) {
613
+ var _a;
614
+ const selection = getOwnerSelection(editableElement);
615
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
616
+ return false;
617
+ }
618
+ const range = selection.getRangeAt(0);
619
+ const container = range.startContainer;
620
+ if (!range.collapsed) {
621
+ return false;
622
+ }
623
+ let isAtEndOfParagraph = false;
624
+ let currentP = null;
625
+ if (isHTMLElement(container) && container.nodeName === 'P') {
626
+ currentP = container;
627
+ const hasOnlyTrailingBR = currentP.childNodes.length === 1 && isBRElement(currentP.firstChild);
628
+ isAtEndOfParagraph = hasOnlyTrailingBR || range.startOffset === currentP.childNodes.length;
629
+ }
630
+ else if (isTextNode(container)) {
631
+ isAtEndOfParagraph = range.startOffset === (((_a = container.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0) && !container.nextSibling;
632
+ let node = container;
633
+ while (node && node.nodeName !== 'P') {
634
+ node = node.parentNode;
635
+ }
636
+ currentP = node;
637
+ }
638
+ if (!isAtEndOfParagraph || !currentP) {
639
+ return false;
640
+ }
641
+ const paragraphs = findAllParagraphs(editableElement);
642
+ const pIndex = Array.from(paragraphs).indexOf(currentP);
643
+ if (pIndex < 0) {
644
+ return false;
645
+ }
646
+ const merged = mergeParagraphs({
647
+ direction: 'forward',
648
+ editableElement,
649
+ tokens,
650
+ currentParagraphIndex: pIndex,
651
+ tokensToText,
652
+ onChange,
653
+ caretController: caretController,
654
+ });
655
+ if (merged) {
656
+ event.preventDefault();
657
+ }
658
+ return merged;
659
+ }
660
+ /** Handles copy/cut events on the contentEditable element. */
661
+ export function handleClipboardEvent(event, editableElement, isCut) {
662
+ const selection = getOwnerSelection(editableElement);
663
+ if (!selection || selection.rangeCount === 0) {
664
+ return;
665
+ }
666
+ const range = selection.getRangeAt(0);
667
+ const fragment = range.cloneContents();
668
+ const paragraphs = findAllParagraphs(fragment);
669
+ const text = paragraphs.length > 0
670
+ ? paragraphs.map(p => stripZeroWidthCharacters(p.textContent || '')).join('\n')
671
+ : stripZeroWidthCharacters(fragment.textContent || '');
672
+ event.clipboardData.setData('text/plain', text);
673
+ event.preventDefault();
674
+ if (isCut) {
675
+ selection.deleteFromDocument();
676
+ }
677
+ }
678
+ //# sourceMappingURL=event-handlers.js.map