@cloudscape-design/components 3.0.1270 → 3.0.1271

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 (178) 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/link/styles.css.js +21 -21
  71. package/link/styles.scoped.css +81 -81
  72. package/link/styles.selectors.js +21 -21
  73. package/package.json +1 -1
  74. package/prompt-input/components/menu-dropdown.d.ts +33 -0
  75. package/prompt-input/components/menu-dropdown.d.ts.map +1 -0
  76. package/prompt-input/components/menu-dropdown.js +48 -0
  77. package/prompt-input/components/menu-dropdown.js.map +1 -0
  78. package/prompt-input/components/textarea-mode.d.ts +15 -0
  79. package/prompt-input/components/textarea-mode.d.ts.map +1 -0
  80. package/prompt-input/components/textarea-mode.js +8 -0
  81. package/prompt-input/components/textarea-mode.js.map +1 -0
  82. package/prompt-input/components/token-mode.d.ts +61 -0
  83. package/prompt-input/components/token-mode.d.ts.map +1 -0
  84. package/prompt-input/components/token-mode.js +37 -0
  85. package/prompt-input/components/token-mode.js.map +1 -0
  86. package/prompt-input/core/caret-controller.d.ts +73 -0
  87. package/prompt-input/core/caret-controller.d.ts.map +1 -0
  88. package/prompt-input/core/caret-controller.js +396 -0
  89. package/prompt-input/core/caret-controller.js.map +1 -0
  90. package/prompt-input/core/caret-spot-utils.d.ts +6 -0
  91. package/prompt-input/core/caret-spot-utils.d.ts.map +1 -0
  92. package/prompt-input/core/caret-spot-utils.js +52 -0
  93. package/prompt-input/core/caret-spot-utils.js.map +1 -0
  94. package/prompt-input/core/caret-utils.d.ts +25 -0
  95. package/prompt-input/core/caret-utils.d.ts.map +1 -0
  96. package/prompt-input/core/caret-utils.js +183 -0
  97. package/prompt-input/core/caret-utils.js.map +1 -0
  98. package/prompt-input/core/constants.d.ts +14 -0
  99. package/prompt-input/core/constants.d.ts.map +1 -0
  100. package/prompt-input/core/constants.js +18 -0
  101. package/prompt-input/core/constants.js.map +1 -0
  102. package/prompt-input/core/dom-utils.d.ts +60 -0
  103. package/prompt-input/core/dom-utils.d.ts.map +1 -0
  104. package/prompt-input/core/dom-utils.js +252 -0
  105. package/prompt-input/core/dom-utils.js.map +1 -0
  106. package/prompt-input/core/event-handlers.d.ts +68 -0
  107. package/prompt-input/core/event-handlers.d.ts.map +1 -0
  108. package/prompt-input/core/event-handlers.js +678 -0
  109. package/prompt-input/core/event-handlers.js.map +1 -0
  110. package/prompt-input/core/menu-state.d.ts +62 -0
  111. package/prompt-input/core/menu-state.d.ts.map +1 -0
  112. package/prompt-input/core/menu-state.js +168 -0
  113. package/prompt-input/core/menu-state.js.map +1 -0
  114. package/prompt-input/core/token-operations.d.ts +21 -0
  115. package/prompt-input/core/token-operations.d.ts.map +1 -0
  116. package/prompt-input/core/token-operations.js +273 -0
  117. package/prompt-input/core/token-operations.js.map +1 -0
  118. package/prompt-input/core/token-renderer.d.ts +26 -0
  119. package/prompt-input/core/token-renderer.d.ts.map +1 -0
  120. package/prompt-input/core/token-renderer.js +230 -0
  121. package/prompt-input/core/token-renderer.js.map +1 -0
  122. package/prompt-input/core/token-utils.d.ts +22 -0
  123. package/prompt-input/core/token-utils.d.ts.map +1 -0
  124. package/prompt-input/core/token-utils.js +262 -0
  125. package/prompt-input/core/token-utils.js.map +1 -0
  126. package/prompt-input/core/trigger-utils.d.ts +18 -0
  127. package/prompt-input/core/trigger-utils.d.ts.map +1 -0
  128. package/prompt-input/core/trigger-utils.js +174 -0
  129. package/prompt-input/core/trigger-utils.js.map +1 -0
  130. package/prompt-input/core/type-guards.d.ts +13 -0
  131. package/prompt-input/core/type-guards.d.ts.map +1 -0
  132. package/prompt-input/core/type-guards.js +36 -0
  133. package/prompt-input/core/type-guards.js.map +1 -0
  134. package/prompt-input/index.d.ts +1 -1
  135. package/prompt-input/index.d.ts.map +1 -1
  136. package/prompt-input/index.js.map +1 -1
  137. package/prompt-input/interfaces.d.ts +356 -7
  138. package/prompt-input/interfaces.d.ts.map +1 -1
  139. package/prompt-input/interfaces.js.map +1 -1
  140. package/prompt-input/internal.d.ts +1 -1
  141. package/prompt-input/internal.d.ts.map +1 -1
  142. package/prompt-input/internal.js +195 -61
  143. package/prompt-input/internal.js.map +1 -1
  144. package/prompt-input/styles.css.js +26 -17
  145. package/prompt-input/styles.scoped.css +152 -39
  146. package/prompt-input/styles.selectors.js +26 -17
  147. package/prompt-input/test-classes/styles.css.js +7 -6
  148. package/prompt-input/test-classes/styles.scoped.css +10 -6
  149. package/prompt-input/test-classes/styles.selectors.js +7 -6
  150. package/prompt-input/tokens/use-shortcuts.d.ts +37 -0
  151. package/prompt-input/tokens/use-shortcuts.d.ts.map +1 -0
  152. package/prompt-input/tokens/use-shortcuts.js +89 -0
  153. package/prompt-input/tokens/use-shortcuts.js.map +1 -0
  154. package/prompt-input/tokens/use-token-mode.d.ts +78 -0
  155. package/prompt-input/tokens/use-token-mode.d.ts.map +1 -0
  156. package/prompt-input/tokens/use-token-mode.js +817 -0
  157. package/prompt-input/tokens/use-token-mode.js.map +1 -0
  158. package/prompt-input/utils/insert-text-content-editable.d.ts +10 -0
  159. package/prompt-input/utils/insert-text-content-editable.d.ts.map +1 -0
  160. package/prompt-input/utils/insert-text-content-editable.js +133 -0
  161. package/prompt-input/utils/insert-text-content-editable.js.map +1 -0
  162. package/tabs/styles.css.js +30 -30
  163. package/tabs/styles.scoped.css +55 -55
  164. package/tabs/styles.selectors.js +30 -30
  165. package/test-utils/dom/hotspot/index.js +1 -2
  166. package/test-utils/dom/hotspot/index.js.map +1 -1
  167. package/test-utils/dom/prompt-input/index.d.ts +53 -1
  168. package/test-utils/dom/prompt-input/index.js +121 -13
  169. package/test-utils/dom/prompt-input/index.js.map +1 -1
  170. package/test-utils/dom/s3-resource-selector/index.js +2 -3
  171. package/test-utils/dom/s3-resource-selector/index.js.map +1 -1
  172. package/test-utils/selectors/hotspot/index.js +1 -2
  173. package/test-utils/selectors/hotspot/index.js.map +1 -1
  174. package/test-utils/selectors/prompt-input/index.d.ts +30 -0
  175. package/test-utils/selectors/prompt-input/index.js +53 -7
  176. package/test-utils/selectors/prompt-input/index.js.map +1 -1
  177. package/test-utils/selectors/s3-resource-selector/index.js +2 -3
  178. package/test-utils/selectors/s3-resource-selector/index.js.map +1 -1
@@ -0,0 +1,22 @@
1
+ import { PromptInputProps } from '../interfaces';
2
+ export { findAdjacentToken } from './dom-utils';
3
+ /** Reorders tokens so all pinned tokens come first, preserving relative order. */
4
+ export declare function enforcePinnedTokenOrdering(tokens: readonly PromptInputProps.InputToken[]): PromptInputProps.InputToken[];
5
+ /** Merges consecutive text tokens into single tokens to avoid DOM fragmentation. */
6
+ export declare function mergeConsecutiveTextTokens(tokens: readonly PromptInputProps.InputToken[]): PromptInputProps.InputToken[];
7
+ export declare function areAllTokensPinned(tokens: readonly PromptInputProps.InputToken[]): boolean;
8
+ /** Checks if a trigger is valid given the menu config, position, and preceding tokens. */
9
+ export declare function validateTrigger(menu: PromptInputProps.MenuDefinition, triggerIndex: number, text: string, precedingTokens: readonly PromptInputProps.InputToken[]): boolean;
10
+ /** Scans text for trigger characters and splits it into text and trigger tokens. */
11
+ export declare function detectTriggersInText(text: string, menus: readonly PromptInputProps.MenuDefinition[], precedingTokens: readonly PromptInputProps.InputToken[], onTriggerDetected?: (detail: PromptInputProps.TriggerDetectedDetail) => boolean, cancelledIds?: Set<string>): PromptInputProps.InputToken[];
12
+ /** Calculates the correct caret position after pinned tokens have been reordered to the front. */
13
+ export declare function getCaretPositionAfterPinnedReorder(originalTokens: readonly PromptInputProps.InputToken[], newTokens: readonly PromptInputProps.InputToken[], caretPosition: number): number;
14
+ /** Maps a caret position from an old token structure to the correct position after structural changes. */
15
+ export declare function getCaretPositionAfterTokenRemoval(savedPosition: number | null, prevTokens: readonly PromptInputProps.InputToken[], newTokens: readonly PromptInputProps.InputToken[]): number | null;
16
+ /**
17
+ * Removes a logical range [start, end) from the token array.
18
+ * Tokens fully within the range are removed. Text tokens partially within
19
+ * the range are trimmed. Reference and trigger tokens are removed if any
20
+ * part of them falls within the range (they're atomic).
21
+ */
22
+ export declare function removeTokenRange(tokens: readonly PromptInputProps.InputToken[], start: number, end: number): PromptInputProps.InputToken[];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-utils.d.ts","sourceRoot":"","sources":["../../../../src/prompt-input/core/token-utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAMjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,kFAAkF;AAClF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GAC7C,gBAAgB,CAAC,UAAU,EAAE,CAmB/B;AAED,oFAAoF;AACpF,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GAC7C,gBAAgB,CAAC,UAAU,EAAE,CAc/B;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GAAG,OAAO,CAE1F;AAED,0FAA0F;AAC1F,wBAAgB,eAAe,CAC7B,IAAI,EAAE,gBAAgB,CAAC,cAAc,EACrC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,eAAe,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GACtD,OAAO,CAUT;AAoDD,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,gBAAgB,CAAC,cAAc,EAAE,EACjD,eAAe,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,EACvD,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,qBAAqB,KAAK,OAAO,EAC/E,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,gBAAgB,CAAC,UAAU,EAAE,CA0C/B;AAED,kGAAkG;AAClG,wBAAgB,kCAAkC,CAChD,cAAc,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,EACtD,SAAS,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,EACjD,aAAa,EAAE,MAAM,GACpB,MAAM,CAsBR;AAED,0GAA0G;AAC1G,wBAAgB,iCAAiC,CAC/C,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,UAAU,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,EAClD,SAAS,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GAChD,MAAM,GAAG,IAAI,CAwEf;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,EAC9C,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,gBAAgB,CAAC,UAAU,EAAE,CA6C/B"}
@@ -0,0 +1,262 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { calculateTotalTokenLength, TOKEN_LENGTHS } from './caret-controller';
4
+ import { generateTokenId } from './dom-utils';
5
+ import { findLastPinnedTokenIndex } from './token-operations';
6
+ import { isBreakTextToken, isPinnedReferenceToken, isReferenceToken, isTextToken, isTriggerToken } from './type-guards';
7
+ export { findAdjacentToken } from './dom-utils';
8
+ /** Reorders tokens so all pinned tokens come first, preserving relative order. */
9
+ export function enforcePinnedTokenOrdering(tokens) {
10
+ const lastPinnedIndex = findLastPinnedTokenIndex(tokens);
11
+ if (lastPinnedIndex === -1) {
12
+ return [...tokens];
13
+ }
14
+ const pinnedTokens = [];
15
+ const restTokens = [];
16
+ for (const token of tokens) {
17
+ if (isPinnedReferenceToken(token)) {
18
+ pinnedTokens.push(token);
19
+ }
20
+ else {
21
+ restTokens.push(token);
22
+ }
23
+ }
24
+ return [...pinnedTokens, ...restTokens];
25
+ }
26
+ /** Merges consecutive text tokens into single tokens to avoid DOM fragmentation. */
27
+ export function mergeConsecutiveTextTokens(tokens) {
28
+ const result = [];
29
+ for (const token of tokens) {
30
+ const lastToken = result[result.length - 1];
31
+ if (lastToken && isTextToken(lastToken) && isTextToken(token)) {
32
+ lastToken.value += token.value;
33
+ }
34
+ else {
35
+ result.push({ ...token });
36
+ }
37
+ }
38
+ return result;
39
+ }
40
+ export function areAllTokensPinned(tokens) {
41
+ return tokens.every(isPinnedReferenceToken);
42
+ }
43
+ /** Checks if a trigger is valid given the menu config, position, and preceding tokens. */
44
+ export function validateTrigger(menu, triggerIndex, text, precedingTokens) {
45
+ const isAtStart = triggerIndex === 0;
46
+ const charBefore = triggerIndex > 0 ? text[triggerIndex - 1] : '';
47
+ const isAfterWhitespace = charBefore.trim() === '';
48
+ if (menu.useAtStart) {
49
+ return isAtStart && areAllTokensPinned(precedingTokens);
50
+ }
51
+ return isAtStart || isAfterWhitespace;
52
+ }
53
+ /** Finds the earliest valid trigger character in text starting from the given position. */
54
+ function findEarliestTrigger(text, position, menus, precedingTokens, onTriggerDetected) {
55
+ var _a;
56
+ let best = null;
57
+ for (const menu of menus) {
58
+ let searchPos = position;
59
+ while (searchPos < text.length) {
60
+ const idx = text.indexOf(menu.trigger, searchPos);
61
+ if (idx === -1) {
62
+ break;
63
+ }
64
+ if (!validateTrigger(menu, idx, text, precedingTokens)) {
65
+ searchPos = idx + menu.trigger.length;
66
+ continue;
67
+ }
68
+ const cancelled = (_a = onTriggerDetected === null || onTriggerDetected === void 0 ? void 0 : onTriggerDetected({ menuId: menu.id, triggerChar: menu.trigger, position: idx })) !== null && _a !== void 0 ? _a : false;
69
+ if (!best || idx < best.index) {
70
+ best = { index: idx, menu, cancelled };
71
+ }
72
+ break;
73
+ }
74
+ }
75
+ return best;
76
+ }
77
+ /** Extracts filter text after a trigger character, stopping at whitespace or another trigger char. */
78
+ function extractFilterText(text, menus) {
79
+ let end = 0;
80
+ while (end < text.length && text[end].trim() !== '') {
81
+ if (menus.some(m => text[end] === m.trigger)) {
82
+ break;
83
+ }
84
+ end++;
85
+ }
86
+ return text.substring(0, end);
87
+ }
88
+ /** Scans text for trigger characters and splits it into text and trigger tokens. */
89
+ export function detectTriggersInText(text, menus, precedingTokens, onTriggerDetected, cancelledIds) {
90
+ const results = [];
91
+ let position = 0;
92
+ while (position < text.length) {
93
+ const match = findEarliestTrigger(text, position, menus, precedingTokens, onTriggerDetected);
94
+ if (!match) {
95
+ results.push({ type: 'text', value: text.substring(position) });
96
+ break;
97
+ }
98
+ const beforeTrigger = text.substring(position, match.index);
99
+ if (beforeTrigger) {
100
+ results.push({ type: 'text', value: beforeTrigger });
101
+ }
102
+ if (match.cancelled) {
103
+ const cancelledId = generateTokenId();
104
+ cancelledIds === null || cancelledIds === void 0 ? void 0 : cancelledIds.add(cancelledId);
105
+ results.push({
106
+ type: 'trigger',
107
+ value: '',
108
+ triggerChar: match.menu.trigger,
109
+ id: cancelledId,
110
+ });
111
+ position = match.index + match.menu.trigger.length;
112
+ }
113
+ else {
114
+ const afterTrigger = text.substring(match.index + match.menu.trigger.length);
115
+ const filterText = afterTrigger && !afterTrigger.startsWith(' ') ? extractFilterText(afterTrigger, menus) : '';
116
+ results.push({
117
+ type: 'trigger',
118
+ value: filterText,
119
+ triggerChar: match.menu.trigger,
120
+ id: generateTokenId(),
121
+ });
122
+ position = match.index + match.menu.trigger.length + filterText.length;
123
+ }
124
+ }
125
+ return results.length > 0 ? results : [{ type: 'text', value: text }];
126
+ }
127
+ /** Calculates the correct caret position after pinned tokens have been reordered to the front. */
128
+ export function getCaretPositionAfterPinnedReorder(originalTokens, newTokens, caretPosition) {
129
+ const totalPinnedCount = newTokens.filter(isPinnedReferenceToken).length;
130
+ let pinnedBeforeCaret = 0;
131
+ let pos = 0;
132
+ for (const token of originalTokens) {
133
+ if (pos >= caretPosition) {
134
+ break;
135
+ }
136
+ if (isPinnedReferenceToken(token)) {
137
+ pinnedBeforeCaret++;
138
+ }
139
+ if (isTextToken(token)) {
140
+ pos += token.value.length;
141
+ }
142
+ else if (isBreakTextToken(token)) {
143
+ pos += TOKEN_LENGTHS.LINE_BREAK;
144
+ }
145
+ else {
146
+ pos += TOKEN_LENGTHS.REFERENCE;
147
+ }
148
+ }
149
+ return caretPosition + (totalPinnedCount - pinnedBeforeCaret);
150
+ }
151
+ /** Maps a caret position from an old token structure to the correct position after structural changes. */
152
+ export function getCaretPositionAfterTokenRemoval(savedPosition, prevTokens, newTokens) {
153
+ if (savedPosition === null) {
154
+ return null;
155
+ }
156
+ const totalLength = calculateTotalTokenLength(newTokens);
157
+ const hasOnlyPinned = newTokens.length > 0 && newTokens.every(isPinnedReferenceToken);
158
+ if (hasOnlyPinned || savedPosition > totalLength) {
159
+ return totalLength;
160
+ }
161
+ const prevTotalLength = calculateTotalTokenLength(prevTokens);
162
+ const lengthDelta = prevTotalLength - totalLength;
163
+ if (lengthDelta === 0) {
164
+ return null;
165
+ }
166
+ // Find where the token arrays first diverge
167
+ let diffPosition = 0;
168
+ const minLen = Math.min(prevTokens.length, newTokens.length);
169
+ for (let i = 0; i < minLen; i++) {
170
+ if (prevTokens[i].type !== newTokens[i].type) {
171
+ break;
172
+ }
173
+ if (isReferenceToken(prevTokens[i]) &&
174
+ isReferenceToken(newTokens[i]) &&
175
+ prevTokens[i].id !== newTokens[i].id) {
176
+ break;
177
+ }
178
+ if (isTriggerToken(prevTokens[i]) &&
179
+ isTriggerToken(newTokens[i]) &&
180
+ prevTokens[i].id !== newTokens[i].id) {
181
+ break;
182
+ }
183
+ // Trigger value changed — let cc.restore() handle it
184
+ if (isTriggerToken(prevTokens[i]) &&
185
+ isTriggerToken(newTokens[i]) &&
186
+ prevTokens[i].id === newTokens[i].id &&
187
+ prevTokens[i].value !== newTokens[i].value) {
188
+ return null;
189
+ }
190
+ if (isTextToken(prevTokens[i])) {
191
+ diffPosition += prevTokens[i].value.length;
192
+ }
193
+ else if (isBreakTextToken(prevTokens[i])) {
194
+ diffPosition += TOKEN_LENGTHS.LINE_BREAK;
195
+ }
196
+ else if (isTriggerToken(prevTokens[i])) {
197
+ diffPosition += TOKEN_LENGTHS.trigger(prevTokens[i].value);
198
+ }
199
+ else {
200
+ diffPosition += TOKEN_LENGTHS.REFERENCE;
201
+ }
202
+ }
203
+ // Tokens were removed — use the saved position if it's plausible
204
+ // (within the old token range, meaning capture() read a valid DOM state),
205
+ // otherwise fall back to the divergence point.
206
+ if (lengthDelta > 0) {
207
+ if (savedPosition <= prevTotalLength && savedPosition <= totalLength) {
208
+ return savedPosition;
209
+ }
210
+ return Math.min(diffPosition, totalLength);
211
+ }
212
+ return null;
213
+ }
214
+ /**
215
+ * Removes a logical range [start, end) from the token array.
216
+ * Tokens fully within the range are removed. Text tokens partially within
217
+ * the range are trimmed. Reference and trigger tokens are removed if any
218
+ * part of them falls within the range (they're atomic).
219
+ */
220
+ export function removeTokenRange(tokens, start, end) {
221
+ if (start >= end) {
222
+ return [...tokens];
223
+ }
224
+ const result = [];
225
+ let pos = 0;
226
+ for (const token of tokens) {
227
+ let tokenLength;
228
+ if (isTextToken(token)) {
229
+ tokenLength = token.value.length;
230
+ }
231
+ else if (isBreakTextToken(token)) {
232
+ tokenLength = TOKEN_LENGTHS.LINE_BREAK;
233
+ }
234
+ else if (isTriggerToken(token)) {
235
+ tokenLength = TOKEN_LENGTHS.trigger(token.value);
236
+ }
237
+ else {
238
+ tokenLength = TOKEN_LENGTHS.REFERENCE;
239
+ }
240
+ const tokenStart = pos;
241
+ const tokenEnd = pos + tokenLength;
242
+ pos = tokenEnd;
243
+ // Fully outside the range — keep as-is
244
+ if (tokenEnd <= start || tokenStart >= end) {
245
+ result.push(token);
246
+ continue;
247
+ }
248
+ // Text token — trim the overlapping portion
249
+ if (isTextToken(token)) {
250
+ const keepBefore = token.value.substring(0, Math.max(0, start - tokenStart));
251
+ const keepAfter = token.value.substring(Math.min(token.value.length, end - tokenStart));
252
+ const remaining = keepBefore + keepAfter;
253
+ if (remaining.length > 0) {
254
+ result.push({ type: 'text', value: remaining });
255
+ }
256
+ continue;
257
+ }
258
+ // Atomic tokens (reference, trigger, break) — remove entirely if any overlap
259
+ }
260
+ return result;
261
+ }
262
+ //# sourceMappingURL=token-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-utils.js","sourceRoot":"","sources":["../../../../src/prompt-input/core/token-utils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { PromptInputProps } from '../interfaces';\nimport { calculateTotalTokenLength, TOKEN_LENGTHS } from './caret-controller';\nimport { generateTokenId } from './dom-utils';\nimport { findLastPinnedTokenIndex } from './token-operations';\nimport { isBreakTextToken, isPinnedReferenceToken, isReferenceToken, isTextToken, isTriggerToken } from './type-guards';\n\nexport { findAdjacentToken } from './dom-utils';\n\n/** Reorders tokens so all pinned tokens come first, preserving relative order. */\nexport function enforcePinnedTokenOrdering(\n tokens: readonly PromptInputProps.InputToken[]\n): PromptInputProps.InputToken[] {\n const lastPinnedIndex = findLastPinnedTokenIndex(tokens);\n\n if (lastPinnedIndex === -1) {\n return [...tokens];\n }\n\n const pinnedTokens: PromptInputProps.InputToken[] = [];\n const restTokens: PromptInputProps.InputToken[] = [];\n\n for (const token of tokens) {\n if (isPinnedReferenceToken(token)) {\n pinnedTokens.push(token);\n } else {\n restTokens.push(token);\n }\n }\n\n return [...pinnedTokens, ...restTokens];\n}\n\n/** Merges consecutive text tokens into single tokens to avoid DOM fragmentation. */\nexport function mergeConsecutiveTextTokens(\n tokens: readonly PromptInputProps.InputToken[]\n): PromptInputProps.InputToken[] {\n const result: PromptInputProps.InputToken[] = [];\n\n for (const token of tokens) {\n const lastToken = result[result.length - 1];\n\n if (lastToken && isTextToken(lastToken) && isTextToken(token)) {\n lastToken.value += token.value;\n } else {\n result.push({ ...token });\n }\n }\n\n return result;\n}\n\nexport function areAllTokensPinned(tokens: readonly PromptInputProps.InputToken[]): boolean {\n return tokens.every(isPinnedReferenceToken);\n}\n\n/** Checks if a trigger is valid given the menu config, position, and preceding tokens. */\nexport function validateTrigger(\n menu: PromptInputProps.MenuDefinition,\n triggerIndex: number,\n text: string,\n precedingTokens: readonly PromptInputProps.InputToken[]\n): boolean {\n const isAtStart = triggerIndex === 0;\n const charBefore = triggerIndex > 0 ? text[triggerIndex - 1] : '';\n const isAfterWhitespace = charBefore.trim() === '';\n\n if (menu.useAtStart) {\n return isAtStart && areAllTokensPinned(precedingTokens);\n }\n\n return isAtStart || isAfterWhitespace;\n}\n\ninterface TriggerMatch {\n index: number;\n menu: PromptInputProps.MenuDefinition;\n cancelled: boolean;\n}\n\n/** Finds the earliest valid trigger character in text starting from the given position. */\nfunction findEarliestTrigger(\n text: string,\n position: number,\n menus: readonly PromptInputProps.MenuDefinition[],\n precedingTokens: readonly PromptInputProps.InputToken[],\n onTriggerDetected?: (detail: PromptInputProps.TriggerDetectedDetail) => boolean\n): TriggerMatch | null {\n let best: TriggerMatch | null = null;\n\n for (const menu of menus) {\n let searchPos = position;\n while (searchPos < text.length) {\n const idx = text.indexOf(menu.trigger, searchPos);\n if (idx === -1) {\n break;\n }\n if (!validateTrigger(menu, idx, text, precedingTokens)) {\n searchPos = idx + menu.trigger.length;\n continue;\n }\n const cancelled = onTriggerDetected?.({ menuId: menu.id, triggerChar: menu.trigger, position: idx }) ?? false;\n if (!best || idx < best.index) {\n best = { index: idx, menu, cancelled };\n }\n break;\n }\n }\n\n return best;\n}\n\n/** Extracts filter text after a trigger character, stopping at whitespace or another trigger char. */\nfunction extractFilterText(text: string, menus: readonly PromptInputProps.MenuDefinition[]): string {\n let end = 0;\n while (end < text.length && text[end].trim() !== '') {\n if (menus.some(m => text[end] === m.trigger)) {\n break;\n }\n end++;\n }\n return text.substring(0, end);\n}\n\n/** Scans text for trigger characters and splits it into text and trigger tokens. */\nexport function detectTriggersInText(\n text: string,\n menus: readonly PromptInputProps.MenuDefinition[],\n precedingTokens: readonly PromptInputProps.InputToken[],\n onTriggerDetected?: (detail: PromptInputProps.TriggerDetectedDetail) => boolean,\n cancelledIds?: Set<string>\n): PromptInputProps.InputToken[] {\n const results: PromptInputProps.InputToken[] = [];\n let position = 0;\n\n while (position < text.length) {\n const match = findEarliestTrigger(text, position, menus, precedingTokens, onTriggerDetected);\n\n if (!match) {\n results.push({ type: 'text', value: text.substring(position) });\n break;\n }\n\n const beforeTrigger = text.substring(position, match.index);\n if (beforeTrigger) {\n results.push({ type: 'text', value: beforeTrigger });\n }\n\n if (match.cancelled) {\n const cancelledId = generateTokenId();\n cancelledIds?.add(cancelledId);\n results.push({\n type: 'trigger',\n value: '',\n triggerChar: match.menu.trigger,\n id: cancelledId,\n });\n position = match.index + match.menu.trigger.length;\n } else {\n const afterTrigger = text.substring(match.index + match.menu.trigger.length);\n const filterText = afterTrigger && !afterTrigger.startsWith(' ') ? extractFilterText(afterTrigger, menus) : '';\n\n results.push({\n type: 'trigger',\n value: filterText,\n triggerChar: match.menu.trigger,\n id: generateTokenId(),\n });\n position = match.index + match.menu.trigger.length + filterText.length;\n }\n }\n\n return results.length > 0 ? results : [{ type: 'text', value: text }];\n}\n\n/** Calculates the correct caret position after pinned tokens have been reordered to the front. */\nexport function getCaretPositionAfterPinnedReorder(\n originalTokens: readonly PromptInputProps.InputToken[],\n newTokens: readonly PromptInputProps.InputToken[],\n caretPosition: number\n): number {\n const totalPinnedCount = newTokens.filter(isPinnedReferenceToken).length;\n let pinnedBeforeCaret = 0;\n let pos = 0;\n\n for (const token of originalTokens) {\n if (pos >= caretPosition) {\n break;\n }\n if (isPinnedReferenceToken(token)) {\n pinnedBeforeCaret++;\n }\n if (isTextToken(token)) {\n pos += token.value.length;\n } else if (isBreakTextToken(token)) {\n pos += TOKEN_LENGTHS.LINE_BREAK;\n } else {\n pos += TOKEN_LENGTHS.REFERENCE;\n }\n }\n\n return caretPosition + (totalPinnedCount - pinnedBeforeCaret);\n}\n\n/** Maps a caret position from an old token structure to the correct position after structural changes. */\nexport function getCaretPositionAfterTokenRemoval(\n savedPosition: number | null,\n prevTokens: readonly PromptInputProps.InputToken[],\n newTokens: readonly PromptInputProps.InputToken[]\n): number | null {\n if (savedPosition === null) {\n return null;\n }\n\n const totalLength = calculateTotalTokenLength(newTokens);\n const hasOnlyPinned = newTokens.length > 0 && newTokens.every(isPinnedReferenceToken);\n\n if (hasOnlyPinned || savedPosition > totalLength) {\n return totalLength;\n }\n\n const prevTotalLength = calculateTotalTokenLength(prevTokens);\n const lengthDelta = prevTotalLength - totalLength;\n\n if (lengthDelta === 0) {\n return null;\n }\n\n // Find where the token arrays first diverge\n let diffPosition = 0;\n const minLen = Math.min(prevTokens.length, newTokens.length);\n\n for (let i = 0; i < minLen; i++) {\n if (prevTokens[i].type !== newTokens[i].type) {\n break;\n }\n if (\n isReferenceToken(prevTokens[i]) &&\n isReferenceToken(newTokens[i]) &&\n (prevTokens[i] as PromptInputProps.ReferenceToken).id !== (newTokens[i] as PromptInputProps.ReferenceToken).id\n ) {\n break;\n }\n if (\n isTriggerToken(prevTokens[i]) &&\n isTriggerToken(newTokens[i]) &&\n (prevTokens[i] as PromptInputProps.TriggerToken).id !== (newTokens[i] as PromptInputProps.TriggerToken).id\n ) {\n break;\n }\n // Trigger value changed — let cc.restore() handle it\n if (\n isTriggerToken(prevTokens[i]) &&\n isTriggerToken(newTokens[i]) &&\n (prevTokens[i] as PromptInputProps.TriggerToken).id === (newTokens[i] as PromptInputProps.TriggerToken).id &&\n (prevTokens[i] as PromptInputProps.TriggerToken).value !== (newTokens[i] as PromptInputProps.TriggerToken).value\n ) {\n return null;\n }\n if (isTextToken(prevTokens[i])) {\n diffPosition += prevTokens[i].value.length;\n } else if (isBreakTextToken(prevTokens[i])) {\n diffPosition += TOKEN_LENGTHS.LINE_BREAK;\n } else if (isTriggerToken(prevTokens[i])) {\n diffPosition += TOKEN_LENGTHS.trigger(prevTokens[i].value);\n } else {\n diffPosition += TOKEN_LENGTHS.REFERENCE;\n }\n }\n\n // Tokens were removed — use the saved position if it's plausible\n // (within the old token range, meaning capture() read a valid DOM state),\n // otherwise fall back to the divergence point.\n if (lengthDelta > 0) {\n if (savedPosition <= prevTotalLength && savedPosition <= totalLength) {\n return savedPosition;\n }\n return Math.min(diffPosition, totalLength);\n }\n\n return null;\n}\n\n/**\n * Removes a logical range [start, end) from the token array.\n * Tokens fully within the range are removed. Text tokens partially within\n * the range are trimmed. Reference and trigger tokens are removed if any\n * part of them falls within the range (they're atomic).\n */\nexport function removeTokenRange(\n tokens: readonly PromptInputProps.InputToken[],\n start: number,\n end: number\n): PromptInputProps.InputToken[] {\n if (start >= end) {\n return [...tokens];\n }\n\n const result: PromptInputProps.InputToken[] = [];\n let pos = 0;\n\n for (const token of tokens) {\n let tokenLength: number;\n if (isTextToken(token)) {\n tokenLength = token.value.length;\n } else if (isBreakTextToken(token)) {\n tokenLength = TOKEN_LENGTHS.LINE_BREAK;\n } else if (isTriggerToken(token)) {\n tokenLength = TOKEN_LENGTHS.trigger(token.value);\n } else {\n tokenLength = TOKEN_LENGTHS.REFERENCE;\n }\n\n const tokenStart = pos;\n const tokenEnd = pos + tokenLength;\n pos = tokenEnd;\n\n // Fully outside the range — keep as-is\n if (tokenEnd <= start || tokenStart >= end) {\n result.push(token);\n continue;\n }\n\n // Text token — trim the overlapping portion\n if (isTextToken(token)) {\n const keepBefore = token.value.substring(0, Math.max(0, start - tokenStart));\n const keepAfter = token.value.substring(Math.min(token.value.length, end - tokenStart));\n const remaining = keepBefore + keepAfter;\n if (remaining.length > 0) {\n result.push({ type: 'text', value: remaining });\n }\n continue;\n }\n\n // Atomic tokens (reference, trigger, break) — remove entirely if any overlap\n }\n\n return result;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { PromptInputProps } from '../interfaces';
2
+ import { CaretController } from './caret-controller';
3
+ import { MenuItemsHandlers, MenuItemsState } from './menu-state';
4
+ interface TriggerSpaceHandlerProps {
5
+ menuItemsState: MenuItemsState;
6
+ menuItemsHandlers: MenuItemsHandlers;
7
+ getMenuStatusType?: () => PromptInputProps.MenuDefinition['statusType'];
8
+ closeMenu: () => void;
9
+ caretController?: CaretController;
10
+ editableElement?: HTMLElement;
11
+ }
12
+ /** Handles space key press when a trigger menu is open. Returns true if handled. */
13
+ export declare function handleSpaceInOpenMenu(event: React.KeyboardEvent, props: TriggerSpaceHandlerProps): boolean;
14
+ /** Handles Delete at the end of a trigger element, removing the leading space from the next text node. */
15
+ export declare function handleDeleteAfterTrigger(event: React.KeyboardEvent<HTMLDivElement>, editableElement: HTMLDivElement): boolean;
16
+ /** Detects structural trigger transitions between old and new token arrays. Returns the caret position, or 0 if none. */
17
+ export declare function detectTriggerTransition(oldTokens: readonly PromptInputProps.InputToken[] | null | undefined, newTokens: readonly PromptInputProps.InputToken[] | null | undefined): number;
18
+ export {};
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger-utils.d.ts","sourceRoot":"","sources":["../../../../src/prompt-input/core/trigger-utils.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAA0B,eAAe,EAAqB,MAAM,oBAAoB,CAAC;AAGhG,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAKjE,UAAU,wBAAwB;IAChC,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,gBAAgB,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;IACxE,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,WAAW,CAAC;CAC/B;AA8BD,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,aAAa,EAAE,KAAK,EAAE,wBAAwB,GAAG,OAAO,CA4E1G;AAED,0GAA0G;AAC1G,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,cAAc,CAAC,EAC1C,eAAe,EAAE,cAAc,GAC9B,OAAO,CAuCT;AAED,yHAAyH;AACzH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GAAG,IAAI,GAAG,SAAS,EACpE,SAAS,EAAE,SAAS,gBAAgB,CAAC,UAAU,EAAE,GAAG,IAAI,GAAG,SAAS,GACnE,MAAM,CAiDR"}
@@ -0,0 +1,174 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { isHTMLElement } from '../../internal/utils/dom';
4
+ import { calculateTokenPosition, getOwnerSelection } from './caret-controller';
5
+ import { ElementType } from './constants';
6
+ import { getTokenType, insertAfter } from './dom-utils';
7
+ import { isTextNode, isTextToken, isTriggerToken } from './type-guards';
8
+ import styles from '../styles.css.js';
9
+ /** Finds the trigger element at the current caret position. */
10
+ /** Finds the trigger element at the current caret position. */
11
+ function findTriggerAtCaret(editableElement) {
12
+ const selection = editableElement ? getOwnerSelection(editableElement) : window.getSelection();
13
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount)) {
14
+ return null;
15
+ }
16
+ const range = selection.getRangeAt(0);
17
+ const parent = isTextNode(range.startContainer) ? range.startContainer.parentElement : null;
18
+ return parent && getTokenType(parent) === ElementType.Trigger ? parent : null;
19
+ }
20
+ /** Finalizes space insertion after a trigger by positioning caret and dispatching input. */
21
+ function finalizeSpaceInsertion(spaceNode, props) {
22
+ if (props.caretController) {
23
+ props.caretController.capture();
24
+ props.caretController.restore(1);
25
+ }
26
+ queueMicrotask(() => {
27
+ var _a;
28
+ const editableElement = (_a = spaceNode.parentElement) === null || _a === void 0 ? void 0 : _a.closest('[contenteditable="true"]');
29
+ if (editableElement) {
30
+ editableElement.dispatchEvent(new Event('input', { bubbles: true }));
31
+ }
32
+ });
33
+ }
34
+ /** Handles space key press when a trigger menu is open. Returns true if handled. */
35
+ export function handleSpaceInOpenMenu(event, props) {
36
+ var _a, _b;
37
+ const { menuItemsState, menuItemsHandlers, getMenuStatusType, closeMenu, editableElement } = props;
38
+ const items = menuItemsState.items;
39
+ const statusType = (_a = getMenuStatusType === null || getMenuStatusType === void 0 ? void 0 : getMenuStatusType()) !== null && _a !== void 0 ? _a : 'finished';
40
+ const isLoading = statusType === 'loading' || statusType === 'pending';
41
+ const ownerElement = editableElement !== null && editableElement !== void 0 ? editableElement : event.currentTarget;
42
+ const triggerElement = findTriggerAtCaret(ownerElement);
43
+ if (!triggerElement) {
44
+ return false;
45
+ }
46
+ const triggerText = triggerElement.textContent || '';
47
+ const triggerChar = triggerText[0];
48
+ const filterText = triggerText.substring(1);
49
+ const ownerDoc = (_b = triggerElement.ownerDocument) !== null && _b !== void 0 ? _b : document;
50
+ // Single match while not loading — auto-select it
51
+ const selectableItems = items.filter(item => item.type !== 'parent');
52
+ if (selectableItems.length === 1 && !isLoading) {
53
+ event.preventDefault();
54
+ return menuItemsHandlers.selectHighlightedOptionWithKeyboard();
55
+ }
56
+ // Double space (filter already ends with space) — close menu and insert one space outside trigger
57
+ if (filterText.endsWith(' ')) {
58
+ event.preventDefault();
59
+ closeMenu();
60
+ const cleanFilterText = filterText.trimEnd();
61
+ triggerElement.textContent = triggerChar + cleanFilterText;
62
+ triggerElement.className = cleanFilterText.length > 0 ? styles['trigger-token'] : '';
63
+ const oneSpace = ownerDoc.createTextNode(' ');
64
+ insertAfter(oneSpace, triggerElement);
65
+ finalizeSpaceInsertion(oneSpace, props);
66
+ return true;
67
+ }
68
+ // Empty filter — space dismisses the trigger
69
+ if (filterText === '') {
70
+ event.preventDefault();
71
+ closeMenu();
72
+ const spaceNode = ownerDoc.createTextNode(' ');
73
+ insertAfter(spaceNode, triggerElement);
74
+ finalizeSpaceInsertion(spaceNode, props);
75
+ return true;
76
+ }
77
+ // Caret right after trigger char with filter text ahead — split filter out of trigger
78
+ const selection = ownerElement ? getOwnerSelection(ownerElement) : window.getSelection();
79
+ const range = (selection === null || selection === void 0 ? void 0 : selection.rangeCount) ? selection.getRangeAt(0) : null;
80
+ if (filterText.length > 0 &&
81
+ range &&
82
+ isTextNode(range.startContainer) &&
83
+ range.startContainer.parentElement === triggerElement &&
84
+ range.startOffset === triggerChar.length) {
85
+ event.preventDefault();
86
+ closeMenu();
87
+ triggerElement.textContent = triggerChar;
88
+ triggerElement.className = '';
89
+ const textAfter = ownerDoc.createTextNode(' ' + filterText);
90
+ insertAfter(textAfter, triggerElement);
91
+ finalizeSpaceInsertion(textAfter, props);
92
+ return true;
93
+ }
94
+ return false;
95
+ }
96
+ /** Handles Delete at the end of a trigger element, removing the leading space from the next text node. */
97
+ export function handleDeleteAfterTrigger(event, editableElement) {
98
+ var _a, _b;
99
+ const selection = getOwnerSelection(editableElement);
100
+ if (!(selection === null || selection === void 0 ? void 0 : selection.rangeCount) || !selection.getRangeAt(0).collapsed) {
101
+ return false;
102
+ }
103
+ const range = selection.getRangeAt(0);
104
+ const { startContainer, startOffset } = range;
105
+ // Find the trigger element the cursor is at the end of
106
+ let triggerElement = null;
107
+ if (isTextNode(startContainer)) {
108
+ const parent = startContainer.parentElement;
109
+ if (parent &&
110
+ getTokenType(parent) === ElementType.Trigger &&
111
+ startOffset === (((_a = startContainer.textContent) === null || _a === void 0 ? void 0 : _a.length) || 0)) {
112
+ triggerElement = parent;
113
+ }
114
+ }
115
+ else if (isHTMLElement(startContainer) && startOffset > 0) {
116
+ const prev = startContainer.childNodes[startOffset - 1];
117
+ if (isHTMLElement(prev) && getTokenType(prev) === ElementType.Trigger) {
118
+ triggerElement = prev;
119
+ }
120
+ }
121
+ const nextText = triggerElement === null || triggerElement === void 0 ? void 0 : triggerElement.nextSibling;
122
+ if (!triggerElement || !nextText || !isTextNode(nextText) || !((_b = nextText.textContent) === null || _b === void 0 ? void 0 : _b.startsWith(' '))) {
123
+ return false;
124
+ }
125
+ event.preventDefault();
126
+ nextText.textContent = nextText.textContent.substring(1);
127
+ if (!nextText.textContent) {
128
+ nextText.remove();
129
+ }
130
+ editableElement.dispatchEvent(new Event('input', { bubbles: true }));
131
+ return true;
132
+ }
133
+ /** Detects structural trigger transitions between old and new token arrays. Returns the caret position, or 0 if none. */
134
+ export function detectTriggerTransition(oldTokens, newTokens) {
135
+ if (!oldTokens || !newTokens) {
136
+ return 0;
137
+ }
138
+ for (let i = 0; i < newTokens.length; i++) {
139
+ const newToken = newTokens[i];
140
+ const oldToken = i < oldTokens.length ? oldTokens[i] : null;
141
+ const prevNewToken = i > 0 ? newTokens[i - 1] : null;
142
+ const prevOldToken = i > 0 && i - 1 < oldTokens.length ? oldTokens[i - 1] : null;
143
+ // Space split: trigger's filter text was pushed into a following text token.
144
+ // Either the text token is new (old array was shorter) or it grew.
145
+ if (isTextToken(newToken) &&
146
+ newToken.value.startsWith(' ') &&
147
+ prevNewToken &&
148
+ isTriggerToken(prevNewToken) &&
149
+ prevNewToken.value === '' &&
150
+ prevOldToken &&
151
+ isTriggerToken(prevOldToken) &&
152
+ prevNewToken.id === prevOldToken.id &&
153
+ prevOldToken.value.length > 0 &&
154
+ (!oldToken || (isTextToken(oldToken) && newToken.value.length > oldToken.value.length))) {
155
+ return calculateTokenPosition(newTokens, i - 1) + 1;
156
+ }
157
+ // Trigger absorbed adjacent text (value grew by more than 1 character)
158
+ if (isTriggerToken(newToken) &&
159
+ oldToken &&
160
+ isTriggerToken(oldToken) &&
161
+ newToken.id === oldToken.id &&
162
+ newToken.value.length > oldToken.value.length + 1) {
163
+ const posBeforeTrigger = i > 0 ? calculateTokenPosition(newTokens, i - 1) : 0;
164
+ if (oldToken.value === '') {
165
+ // Merge from empty trigger → caret after trigger char
166
+ return posBeforeTrigger + newToken.triggerChar.length;
167
+ }
168
+ // Merge from non-empty trigger → caret at merge point
169
+ return posBeforeTrigger + newToken.triggerChar.length + oldToken.value.length;
170
+ }
171
+ }
172
+ return 0;
173
+ }
174
+ //# sourceMappingURL=trigger-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger-utils.js","sourceRoot":"","sources":["../../../../src/prompt-input/core/trigger-utils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { isHTMLElement } from '../../internal/utils/dom';\nimport { PromptInputProps } from '../interfaces';\nimport { calculateTokenPosition, CaretController, getOwnerSelection } from './caret-controller';\nimport { ElementType } from './constants';\nimport { getTokenType, insertAfter } from './dom-utils';\nimport { MenuItemsHandlers, MenuItemsState } from './menu-state';\nimport { isTextNode, isTextToken, isTriggerToken } from './type-guards';\n\nimport styles from '../styles.css.js';\n\ninterface TriggerSpaceHandlerProps {\n menuItemsState: MenuItemsState;\n menuItemsHandlers: MenuItemsHandlers;\n getMenuStatusType?: () => PromptInputProps.MenuDefinition['statusType'];\n closeMenu: () => void;\n caretController?: CaretController;\n editableElement?: HTMLElement;\n}\n\n/** Finds the trigger element at the current caret position. */\n/** Finds the trigger element at the current caret position. */\nfunction findTriggerAtCaret(editableElement?: HTMLElement): HTMLElement | null {\n const selection = editableElement ? getOwnerSelection(editableElement) : window.getSelection();\n if (!selection?.rangeCount) {\n return null;\n }\n\n const range = selection.getRangeAt(0);\n const parent = isTextNode(range.startContainer) ? range.startContainer.parentElement : null;\n return parent && getTokenType(parent) === ElementType.Trigger ? parent : null;\n}\n\n/** Finalizes space insertion after a trigger by positioning caret and dispatching input. */\nfunction finalizeSpaceInsertion(spaceNode: Text, props: Pick<TriggerSpaceHandlerProps, 'caretController'>): void {\n if (props.caretController) {\n props.caretController.capture();\n props.caretController.restore(1);\n }\n\n queueMicrotask(() => {\n const editableElement = spaceNode.parentElement?.closest('[contenteditable=\"true\"]') as HTMLElement;\n if (editableElement) {\n editableElement.dispatchEvent(new Event('input', { bubbles: true }));\n }\n });\n}\n\n/** Handles space key press when a trigger menu is open. Returns true if handled. */\nexport function handleSpaceInOpenMenu(event: React.KeyboardEvent, props: TriggerSpaceHandlerProps): boolean {\n const { menuItemsState, menuItemsHandlers, getMenuStatusType, closeMenu, editableElement } = props;\n const items = menuItemsState.items;\n const statusType = getMenuStatusType?.() ?? 'finished';\n const isLoading = statusType === 'loading' || statusType === 'pending';\n\n const ownerElement = editableElement ?? (event.currentTarget as HTMLElement);\n const triggerElement = findTriggerAtCaret(ownerElement);\n if (!triggerElement) {\n return false;\n }\n\n const triggerText = triggerElement.textContent || '';\n const triggerChar = triggerText[0];\n const filterText = triggerText.substring(1);\n const ownerDoc = triggerElement.ownerDocument ?? document;\n\n // Single match while not loading — auto-select it\n const selectableItems = items.filter(item => item.type !== 'parent');\n if (selectableItems.length === 1 && !isLoading) {\n event.preventDefault();\n return menuItemsHandlers.selectHighlightedOptionWithKeyboard();\n }\n\n // Double space (filter already ends with space) — close menu and insert one space outside trigger\n if (filterText.endsWith(' ')) {\n event.preventDefault();\n closeMenu();\n\n const cleanFilterText = filterText.trimEnd();\n triggerElement.textContent = triggerChar + cleanFilterText;\n triggerElement.className = cleanFilterText.length > 0 ? styles['trigger-token'] : '';\n\n const oneSpace = ownerDoc.createTextNode(' ');\n insertAfter(oneSpace, triggerElement);\n finalizeSpaceInsertion(oneSpace, props);\n\n return true;\n }\n\n // Empty filter — space dismisses the trigger\n if (filterText === '') {\n event.preventDefault();\n closeMenu();\n\n const spaceNode = ownerDoc.createTextNode(' ');\n insertAfter(spaceNode, triggerElement);\n finalizeSpaceInsertion(spaceNode, props);\n\n return true;\n }\n\n // Caret right after trigger char with filter text ahead — split filter out of trigger\n const selection = ownerElement ? getOwnerSelection(ownerElement) : window.getSelection();\n const range = selection?.rangeCount ? selection.getRangeAt(0) : null;\n if (\n filterText.length > 0 &&\n range &&\n isTextNode(range.startContainer) &&\n range.startContainer.parentElement === triggerElement &&\n range.startOffset === triggerChar.length\n ) {\n event.preventDefault();\n closeMenu();\n\n triggerElement.textContent = triggerChar;\n triggerElement.className = '';\n\n const textAfter = ownerDoc.createTextNode(' ' + filterText);\n insertAfter(textAfter, triggerElement);\n finalizeSpaceInsertion(textAfter, props);\n\n return true;\n }\n\n return false;\n}\n\n/** Handles Delete at the end of a trigger element, removing the leading space from the next text node. */\nexport function handleDeleteAfterTrigger(\n event: React.KeyboardEvent<HTMLDivElement>,\n editableElement: HTMLDivElement\n): boolean {\n const selection = getOwnerSelection(editableElement);\n if (!selection?.rangeCount || !selection.getRangeAt(0).collapsed) {\n return false;\n }\n\n const range = selection.getRangeAt(0);\n const { startContainer, startOffset } = range;\n\n // Find the trigger element the cursor is at the end of\n let triggerElement: HTMLElement | null = null;\n if (isTextNode(startContainer)) {\n const parent = startContainer.parentElement;\n if (\n parent &&\n getTokenType(parent) === ElementType.Trigger &&\n startOffset === (startContainer.textContent?.length || 0)\n ) {\n triggerElement = parent;\n }\n } else if (isHTMLElement(startContainer) && startOffset > 0) {\n const prev = startContainer.childNodes[startOffset - 1];\n if (isHTMLElement(prev) && getTokenType(prev) === ElementType.Trigger) {\n triggerElement = prev;\n }\n }\n\n const nextText = triggerElement?.nextSibling;\n if (!triggerElement || !nextText || !isTextNode(nextText) || !nextText.textContent?.startsWith(' ')) {\n return false;\n }\n\n event.preventDefault();\n nextText.textContent = nextText.textContent.substring(1);\n if (!nextText.textContent) {\n nextText.remove();\n }\n editableElement.dispatchEvent(new Event('input', { bubbles: true }));\n return true;\n}\n\n/** Detects structural trigger transitions between old and new token arrays. Returns the caret position, or 0 if none. */\nexport function detectTriggerTransition(\n oldTokens: readonly PromptInputProps.InputToken[] | null | undefined,\n newTokens: readonly PromptInputProps.InputToken[] | null | undefined\n): number {\n if (!oldTokens || !newTokens) {\n return 0;\n }\n\n for (let i = 0; i < newTokens.length; i++) {\n const newToken = newTokens[i];\n const oldToken = i < oldTokens.length ? oldTokens[i] : null;\n const prevNewToken = i > 0 ? newTokens[i - 1] : null;\n const prevOldToken = i > 0 && i - 1 < oldTokens.length ? oldTokens[i - 1] : null;\n\n // Space split: trigger's filter text was pushed into a following text token.\n // Either the text token is new (old array was shorter) or it grew.\n if (\n isTextToken(newToken) &&\n newToken.value.startsWith(' ') &&\n prevNewToken &&\n isTriggerToken(prevNewToken) &&\n prevNewToken.value === '' &&\n prevOldToken &&\n isTriggerToken(prevOldToken) &&\n prevNewToken.id === prevOldToken.id &&\n prevOldToken.value.length > 0 &&\n (!oldToken || (isTextToken(oldToken) && newToken.value.length > oldToken.value.length))\n ) {\n return calculateTokenPosition(newTokens, i - 1) + 1;\n }\n\n // Trigger absorbed adjacent text (value grew by more than 1 character)\n if (\n isTriggerToken(newToken) &&\n oldToken &&\n isTriggerToken(oldToken) &&\n newToken.id === oldToken.id &&\n newToken.value.length > oldToken.value.length + 1\n ) {\n const posBeforeTrigger = i > 0 ? calculateTokenPosition(newTokens, i - 1) : 0;\n\n if (oldToken.value === '') {\n // Merge from empty trigger → caret after trigger char\n return posBeforeTrigger + newToken.triggerChar.length;\n }\n\n // Merge from non-empty trigger → caret at merge point\n return posBeforeTrigger + newToken.triggerChar.length + oldToken.value.length;\n }\n }\n\n return 0;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { PromptInputProps } from '../interfaces';
2
+ /** Checks if a node is a Text node. */
3
+ export declare function isTextNode(node: Node | null): node is Text;
4
+ /**
5
+ * Checks if a node is a BR element, optionally matching a specific data-id.
6
+ * @param dataId optional data-id to match (e.g., ElementType.TrailingBreak)
7
+ */
8
+ export declare function isBRElement(node: Node | null | undefined, dataId?: string): node is HTMLBRElement;
9
+ export declare function isTextToken(token: PromptInputProps.InputToken): token is PromptInputProps.TextToken;
10
+ export declare function isBreakTextToken(token: PromptInputProps.InputToken): token is PromptInputProps.TextToken;
11
+ export declare function isTriggerToken(token: PromptInputProps.InputToken): token is PromptInputProps.TriggerToken;
12
+ export declare function isReferenceToken(token: PromptInputProps.InputToken): token is PromptInputProps.ReferenceToken;
13
+ export declare function isPinnedReferenceToken(token: PromptInputProps.InputToken): token is PromptInputProps.ReferenceToken;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-guards.d.ts","sourceRoot":"","sources":["../../../../src/prompt-input/core/type-guards.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,uCAAuC;AACvC,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,IAAI,IAAI,CAE1D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,IAAI,aAAa,CAQjG;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG,KAAK,IAAI,gBAAgB,CAAC,SAAS,CAEnG;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG,KAAK,IAAI,gBAAgB,CAAC,SAAS,CAExG;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG,KAAK,IAAI,gBAAgB,CAAC,YAAY,CAEzG;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG,KAAK,IAAI,gBAAgB,CAAC,cAAc,CAE7G;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,gBAAgB,CAAC,UAAU,GAAG,KAAK,IAAI,gBAAgB,CAAC,cAAc,CAEnH"}
@@ -0,0 +1,36 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { isHTMLElement } from '../../internal/utils/dom';
4
+ /** Checks if a node is a Text node. */
5
+ export function isTextNode(node) {
6
+ return (node === null || node === void 0 ? void 0 : node.nodeType) === Node.TEXT_NODE;
7
+ }
8
+ /**
9
+ * Checks if a node is a BR element, optionally matching a specific data-id.
10
+ * @param dataId optional data-id to match (e.g., ElementType.TrailingBreak)
11
+ */
12
+ export function isBRElement(node, dataId) {
13
+ if ((node === null || node === void 0 ? void 0 : node.nodeName) !== 'BR' || !isHTMLElement(node)) {
14
+ return false;
15
+ }
16
+ if (dataId !== undefined) {
17
+ return node.getAttribute('data-id') === dataId;
18
+ }
19
+ return true;
20
+ }
21
+ export function isTextToken(token) {
22
+ return token.type === 'text';
23
+ }
24
+ export function isBreakTextToken(token) {
25
+ return token.type === 'break';
26
+ }
27
+ export function isTriggerToken(token) {
28
+ return token.type === 'trigger';
29
+ }
30
+ export function isReferenceToken(token) {
31
+ return token.type === 'reference';
32
+ }
33
+ export function isPinnedReferenceToken(token) {
34
+ return isReferenceToken(token) && token.pinned === true;
35
+ }
36
+ //# sourceMappingURL=type-guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-guards.js","sourceRoot":"","sources":["../../../../src/prompt-input/core/type-guards.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,sCAAsC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGzD,uCAAuC;AACvC,MAAM,UAAU,UAAU,CAAC,IAAiB;IAC1C,OAAO,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,QAAQ,MAAK,IAAI,CAAC,SAAS,CAAC;AAC3C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAA6B,EAAE,MAAe;IACxE,IAAI,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,QAAQ,MAAK,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,KAAK,MAAM,CAAC;IACjD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAkC;IAC5D,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAkC;IACjE,OAAO,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAkC;IAC/D,OAAO,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAkC;IACjE,OAAO,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAkC;IACvE,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC;AAC1D,CAAC","sourcesContent":["// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { isHTMLElement } from '../../internal/utils/dom';\nimport { PromptInputProps } from '../interfaces';\n\n/** Checks if a node is a Text node. */\nexport function isTextNode(node: Node | null): node is Text {\n return node?.nodeType === Node.TEXT_NODE;\n}\n\n/**\n * Checks if a node is a BR element, optionally matching a specific data-id.\n * @param dataId optional data-id to match (e.g., ElementType.TrailingBreak)\n */\nexport function isBRElement(node: Node | null | undefined, dataId?: string): node is HTMLBRElement {\n if (node?.nodeName !== 'BR' || !isHTMLElement(node)) {\n return false;\n }\n if (dataId !== undefined) {\n return node.getAttribute('data-id') === dataId;\n }\n return true;\n}\n\nexport function isTextToken(token: PromptInputProps.InputToken): token is PromptInputProps.TextToken {\n return token.type === 'text';\n}\n\nexport function isBreakTextToken(token: PromptInputProps.InputToken): token is PromptInputProps.TextToken {\n return token.type === 'break';\n}\n\nexport function isTriggerToken(token: PromptInputProps.InputToken): token is PromptInputProps.TriggerToken {\n return token.type === 'trigger';\n}\n\nexport function isReferenceToken(token: PromptInputProps.InputToken): token is PromptInputProps.ReferenceToken {\n return token.type === 'reference';\n}\n\nexport function isPinnedReferenceToken(token: PromptInputProps.InputToken): token is PromptInputProps.ReferenceToken {\n return isReferenceToken(token) && token.pinned === true;\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
2
  import { PromptInputProps } from './interfaces';
3
3
  export { PromptInputProps };
4
- declare const PromptInput: React.ForwardRefExoticComponent<PromptInputProps & React.RefAttributes<PromptInputProps.Ref>>;
4
+ declare const PromptInput: React.ForwardRefExoticComponent<PromptInputProps & React.RefAttributes<HTMLInputElement | HTMLTextAreaElement | PromptInputProps.Ref>>;
5
5
  export default PromptInput;