@assistant-ui/react 0.15.23 → 0.15.25

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 (212) hide show
  1. package/README.md +44 -23
  2. package/dist/assistant-transport.js.map +1 -1
  3. package/dist/cloud-renderer/CloudRendererHost.js.map +1 -1
  4. package/dist/context/providers/MessageProvider.js.map +1 -1
  5. package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
  6. package/dist/hooks/useToolCallElapsed.d.ts +3 -1
  7. package/dist/hooks/useToolCallElapsed.d.ts.map +1 -1
  8. package/dist/hooks/useToolCallElapsed.js +7 -8
  9. package/dist/hooks/useToolCallElapsed.js.map +1 -1
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -3
  13. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  14. package/dist/mcp-apps/app-frame.js.map +1 -1
  15. package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
  16. package/dist/model-context/makeAssistantVisible.d.ts +1 -0
  17. package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
  18. package/dist/model-context/makeAssistantVisible.js +31 -12
  19. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  20. package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
  21. package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
  22. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  23. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
  24. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
  25. package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
  26. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
  27. package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -1
  28. package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
  29. package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
  30. package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
  31. package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -1
  32. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -1
  33. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -1
  34. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -1
  35. package/dist/primitives/actionBarMore/scope.js.map +1 -1
  36. package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -1
  37. package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
  38. package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
  39. package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -1
  40. package/dist/primitives/assistantModal/popoverRenderPrimitives.js.map +1 -1
  41. package/dist/primitives/assistantModal/scope.js.map +1 -1
  42. package/dist/primitives/attachment/AttachmentName.js.map +1 -1
  43. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  44. package/dist/primitives/attachment/AttachmentRoot.js.map +1 -1
  45. package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
  46. package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -1
  47. package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
  48. package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -1
  49. package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
  50. package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
  51. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  52. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -1
  53. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  54. package/dist/primitives/composer/ComposerCancel.js.map +1 -1
  55. package/dist/primitives/composer/ComposerDictate.js.map +1 -1
  56. package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
  57. package/dist/primitives/composer/ComposerInput.d.ts +1 -1
  58. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  59. package/dist/primitives/composer/ComposerInput.js +4 -3
  60. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  61. package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -1
  62. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  63. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  64. package/dist/primitives/composer/ComposerSend.js.map +1 -1
  65. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  66. package/dist/primitives/composer/trigger/TriggerPopover.d.ts +11 -11
  67. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
  68. package/dist/primitives/composer/trigger/TriggerPopover.js +62 -60
  69. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  70. package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts +8 -8
  71. package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts.map +1 -1
  72. package/dist/primitives/composer/trigger/TriggerPopoverAction.js +6 -6
  73. package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
  74. package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -1
  75. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +3 -3
  76. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
  77. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
  78. package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts +9 -9
  79. package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts.map +1 -1
  80. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +7 -7
  81. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
  82. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +5 -5
  83. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
  84. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
  85. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
  86. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +8 -8
  87. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +8 -8
  88. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
  89. package/dist/primitives/composer/trigger/index.d.ts +1 -1
  90. package/dist/primitives/composer/trigger/index.js.map +1 -1
  91. package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
  92. package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
  93. package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
  94. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  95. package/dist/primitives/composer/useComposerInputState.js.map +1 -1
  96. package/dist/primitives/composer.d.ts +20 -1
  97. package/dist/primitives/composer.d.ts.map +1 -1
  98. package/dist/primitives/composer.js +8 -1
  99. package/dist/primitives/composer.js.map +1 -1
  100. package/dist/primitives/dropdownMenuRenderPrimitives.js.map +1 -1
  101. package/dist/primitives/error/ErrorMessage.js.map +1 -1
  102. package/dist/primitives/error/ErrorRoot.js.map +1 -1
  103. package/dist/primitives/message/MessageIf.js.map +1 -1
  104. package/dist/primitives/message/MessageParts.js.map +1 -1
  105. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  106. package/dist/primitives/message/MessagePartsGrouped.js +42 -17
  107. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  108. package/dist/primitives/message/MessageRoot.js.map +1 -1
  109. package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
  110. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  111. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  112. package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
  113. package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
  114. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  115. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  116. package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
  117. package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
  118. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  119. package/dist/primitives/thread/ThreadIf.js.map +1 -1
  120. package/dist/primitives/thread/ThreadLoadEarlier.d.ts +19 -0
  121. package/dist/primitives/thread/ThreadLoadEarlier.d.ts.map +1 -0
  122. package/dist/primitives/thread/ThreadLoadEarlier.js +36 -0
  123. package/dist/primitives/thread/ThreadLoadEarlier.js.map +1 -0
  124. package/dist/primitives/thread/ThreadMessages.d.ts +1 -1
  125. package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
  126. package/dist/primitives/thread/ThreadMessages.js +2 -2
  127. package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
  128. package/dist/primitives/thread/ThreadRoot.js +2 -0
  129. package/dist/primitives/thread/ThreadRoot.js.map +1 -1
  130. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
  131. package/dist/primitives/thread/ThreadSuggestion.js.map +1 -1
  132. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  133. package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
  134. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
  135. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  136. package/dist/primitives/thread/useThreadViewportAutoScroll.js +111 -2
  137. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  138. package/dist/primitives/thread.d.ts +2 -0
  139. package/dist/primitives/thread.d.ts.map +1 -1
  140. package/dist/primitives/thread.js +5 -2
  141. package/dist/primitives/thread.js.map +1 -1
  142. package/dist/primitives/threadList/ThreadListLoadMore.js.map +1 -1
  143. package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
  144. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
  145. package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -1
  146. package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -1
  147. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  148. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  149. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
  150. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
  151. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -1
  152. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
  153. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -1
  154. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
  155. package/dist/primitives/threadListItemMore/scope.js.map +1 -1
  156. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
  157. package/dist/sandbox-host/SandboxHost.js +6 -1
  158. package/dist/sandbox-host/SandboxHost.js.map +1 -1
  159. package/dist/unstable/useMessageStallDetection.d.ts.map +1 -1
  160. package/dist/unstable/useMessageStallDetection.js +10 -6
  161. package/dist/unstable/useMessageStallDetection.js.map +1 -1
  162. package/dist/unstable/useModelContextSnapshot.js.map +1 -1
  163. package/dist/unstable/webmcp/WebMcpRegistrationResource.js.map +1 -1
  164. package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -1
  165. package/dist/utils/Primitive.js.map +1 -1
  166. package/dist/utils/createActionButton.js.map +1 -1
  167. package/dist/utils/hooks/useManagedRef.js.map +1 -1
  168. package/dist/utils/hooks/useMediaQuery.js.map +1 -1
  169. package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
  170. package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
  171. package/dist/utils/hooks/useSizeHandle.js.map +1 -1
  172. package/dist/utils/isCompositionKey.d.ts +2 -0
  173. package/dist/utils/isCompositionKey.d.ts.map +1 -0
  174. package/dist/utils/isCompositionKey.js +6 -0
  175. package/dist/utils/isCompositionKey.js.map +1 -0
  176. package/dist/utils/smooth/SmoothContext.js.map +1 -1
  177. package/dist/utils/smooth/useSmooth.js +6 -6
  178. package/dist/utils/smooth/useSmooth.js.map +1 -1
  179. package/package.json +12 -11
  180. package/src/hooks/useToolCallElapsed.test.tsx +35 -0
  181. package/src/hooks/useToolCallElapsed.ts +9 -7
  182. package/src/index.ts +28 -3
  183. package/src/model-context/makeAssistantVisible.test.tsx +315 -5
  184. package/src/model-context/makeAssistantVisible.tsx +43 -7
  185. package/src/primitives/composer/ComposerInput.test.tsx +33 -1
  186. package/src/primitives/composer/ComposerInput.tsx +4 -3
  187. package/src/primitives/composer/trigger/TriggerPopover.tsx +11 -11
  188. package/src/primitives/composer/trigger/TriggerPopoverAction.tsx +10 -13
  189. package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +2 -2
  190. package/src/primitives/composer/trigger/TriggerPopoverDirective.tsx +11 -14
  191. package/src/primitives/composer/trigger/TriggerPopoverExports.test.ts +61 -0
  192. package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +3 -3
  193. package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +8 -8
  194. package/src/primitives/composer.ts +21 -1
  195. package/src/primitives/message/MessagePartsGrouped.test.tsx +271 -1
  196. package/src/primitives/message/MessagePartsGrouped.tsx +14 -6
  197. package/src/primitives/message/MessageRoot.test.tsx +125 -2
  198. package/src/primitives/thread/ThreadLoadEarlier.tsx +35 -0
  199. package/src/primitives/thread/ThreadMessages.tsx +1 -0
  200. package/src/primitives/thread/ThreadRoot.test.tsx +33 -0
  201. package/src/primitives/thread/ThreadRoot.tsx +3 -0
  202. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +256 -5
  203. package/src/primitives/thread/useThreadViewportAutoScroll.ts +147 -1
  204. package/src/primitives/thread.ts +2 -0
  205. package/src/sandbox-host/SandboxHost.test.tsx +34 -0
  206. package/src/sandbox-host/SandboxHost.tsx +8 -4
  207. package/src/tests/toolCallTiming.test.tsx +19 -2
  208. package/src/unstable/useMessageStallDetection.test.tsx +40 -0
  209. package/src/unstable/useMessageStallDetection.ts +10 -6
  210. package/src/utils/isCompositionKey.ts +5 -0
  211. package/src/utils/smooth/useSmooth.test.tsx +16 -5
  212. package/src/utils/smooth/useSmooth.ts +7 -7
@@ -15,6 +15,27 @@ import { useAui } from "@assistant-ui/store";
15
15
  import { useComposedRefs } from "radix-ui/internal";
16
16
  import { tool } from "@assistant-ui/core";
17
17
 
18
+ const DEFAULT_ACTION_SETTLE_DELAY_MS = 2000;
19
+ const MAX_ACTION_SETTLE_DELAY_MS = 2_147_483_647;
20
+
21
+ const waitForSettle = async (settleDelayMs: number) => {
22
+ const delay =
23
+ Number.isFinite(settleDelayMs) &&
24
+ settleDelayMs >= 0 &&
25
+ settleDelayMs <= MAX_ACTION_SETTLE_DELAY_MS
26
+ ? settleDelayMs
27
+ : DEFAULT_ACTION_SETTLE_DELAY_MS;
28
+ if (delay === 0) return;
29
+ await new Promise<void>((resolve) => setTimeout(resolve, delay));
30
+ };
31
+
32
+ const readActionSettleDelay = (element: HTMLElement) => {
33
+ const value = element.dataset.actionSettleDelay;
34
+ return value === undefined || value.trim() === ""
35
+ ? DEFAULT_ACTION_SETTLE_DELAY_MS
36
+ : Number(value);
37
+ };
38
+
18
39
  const click = tool({
19
40
  parameters: {
20
41
  type: "object",
@@ -30,9 +51,7 @@ const click = tool({
30
51
  const el = document.querySelector(`[data-click-id='${escapedClickId}']`);
31
52
  if (el instanceof HTMLElement) {
32
53
  el.click();
33
-
34
- // todo make adjustable
35
- await new Promise((resolve) => setTimeout(resolve, 2000));
54
+ await waitForSettle(readActionSettleDelay(el));
36
55
  return {};
37
56
  } else {
38
57
  return "Element not found";
@@ -77,9 +96,7 @@ const edit = tool({
77
96
  setNativeValue(el, value);
78
97
  el.dispatchEvent(new Event("input", { bubbles: true }));
79
98
  el.dispatchEvent(new Event("change", { bubbles: true }));
80
-
81
- // todo make adjustable
82
- await new Promise((resolve) => setTimeout(resolve, 2000));
99
+ await waitForSettle(readActionSettleDelay(el));
83
100
  return {};
84
101
  } else {
85
102
  return "Element not found";
@@ -91,8 +108,23 @@ const ReadableContext = createContext<boolean>(false);
91
108
 
92
109
  export const makeAssistantVisible = <T extends ComponentType<any>>(
93
110
  Component: T,
94
- config?: { clickable?: boolean | undefined; editable?: boolean | undefined },
111
+ config?: {
112
+ clickable?: boolean | undefined;
113
+ editable?: boolean | undefined;
114
+ settleDelayMs?: number | undefined;
115
+ },
95
116
  ) => {
117
+ const settleDelayMs = config?.settleDelayMs ?? DEFAULT_ACTION_SETTLE_DELAY_MS;
118
+ if (
119
+ !Number.isFinite(settleDelayMs) ||
120
+ settleDelayMs < 0 ||
121
+ settleDelayMs > MAX_ACTION_SETTLE_DELAY_MS
122
+ ) {
123
+ throw new RangeError(
124
+ `settleDelayMs must be between 0 and ${MAX_ACTION_SETTLE_DELAY_MS}`,
125
+ );
126
+ }
127
+
96
128
  const ReadableComponent = forwardRef(
97
129
  (props: PropsWithoutRef<T>, outerRef: ForwardedRef<any>) => {
98
130
  const isNestedReadable = useContext(ReadableContext);
@@ -127,6 +159,10 @@ export const makeAssistantVisible = <T extends ComponentType<any>>(
127
159
  {...(props as any)}
128
160
  {...(config?.clickable ? { "data-click-id": clickId } : {})}
129
161
  {...(config?.editable ? { "data-edit-id": clickId } : {})}
162
+ {...(config?.settleDelayMs !== undefined &&
163
+ (config.clickable || config.editable)
164
+ ? { "data-action-settle-delay": settleDelayMs }
165
+ : {})}
130
166
  ref={ref}
131
167
  />
132
168
  </ReadableContext.Provider>
@@ -133,6 +133,7 @@ const fireKeyDown = (
133
133
  ctrlKey?: boolean;
134
134
  metaKey?: boolean;
135
135
  isComposing?: boolean;
136
+ keyCode?: number;
136
137
  } = {},
137
138
  ): KeyboardEvent => {
138
139
  const event = new KeyboardEvent("keydown", {
@@ -143,6 +144,7 @@ const fireKeyDown = (
143
144
  ctrlKey: opts.ctrlKey ?? false,
144
145
  metaKey: opts.metaKey ?? false,
145
146
  isComposing: opts.isComposing ?? false,
147
+ keyCode: opts.keyCode ?? 0,
146
148
  });
147
149
  textarea.dispatchEvent(event);
148
150
  return event;
@@ -441,6 +443,20 @@ describe("ComposerPrimitiveInput", () => {
441
443
  expect(event.defaultPrevented).toBe(false);
442
444
  });
443
445
 
446
+ it("ignores the Enter that commits an IME composition after compositionend (Safari)", async () => {
447
+ const textarea = await mount();
448
+
449
+ let event!: KeyboardEvent;
450
+ await act(async () => {
451
+ fireCompositionStart(textarea);
452
+ fireCompositionEnd(textarea, "日本語");
453
+ event = fireKeyDown(textarea, { key: "Enter", keyCode: 229 });
454
+ });
455
+
456
+ expect(requestSubmitSpy).not.toHaveBeenCalled();
457
+ expect(event.defaultPrevented).toBe(false);
458
+ });
459
+
444
460
  it("submitMode='ctrlEnter' submits on Cmd/Ctrl+Enter but not on plain Enter", async () => {
445
461
  const textarea = await mount({ submitMode: "ctrlEnter" });
446
462
 
@@ -566,13 +582,14 @@ describe("ComposerPrimitiveInput", () => {
566
582
  describe("escape behavior", () => {
567
583
  const fireEscape = (
568
584
  textarea: HTMLTextAreaElement,
569
- opts: { isComposing?: boolean } = {},
585
+ opts: { isComposing?: boolean; keyCode?: number } = {},
570
586
  ): KeyboardEvent => {
571
587
  const event = new KeyboardEvent("keydown", {
572
588
  bubbles: true,
573
589
  cancelable: true,
574
590
  key: "Escape",
575
591
  isComposing: opts.isComposing ?? false,
592
+ keyCode: opts.keyCode ?? 0,
576
593
  });
577
594
  textarea.dispatchEvent(event);
578
595
  escapeKeydownHandler?.(event);
@@ -604,6 +621,21 @@ describe("ComposerPrimitiveInput", () => {
604
621
  expect(cancelSpy).not.toHaveBeenCalled();
605
622
  expect(event.defaultPrevented).toBe(false);
606
623
  });
624
+
625
+ it("ignores the Escape that cancels an IME composition after compositionend (Safari)", async () => {
626
+ composerState.canCancel = true;
627
+ const textarea = await mount();
628
+
629
+ let event!: KeyboardEvent;
630
+ await act(async () => {
631
+ fireCompositionStart(textarea);
632
+ fireCompositionEnd(textarea, "");
633
+ event = fireEscape(textarea, { keyCode: 229 });
634
+ });
635
+
636
+ expect(cancelSpy).not.toHaveBeenCalled();
637
+ expect(event.defaultPrevented).toBe(false);
638
+ });
607
639
  });
608
640
 
609
641
  describe("paste attachments", () => {
@@ -22,6 +22,7 @@ import { useEscapeKeydown } from "radix-ui/internal";
22
22
  import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
23
23
  import { useMediaQuery } from "../../utils/hooks/useMediaQuery";
24
24
  import { renderSlot } from "../../utils/Primitive";
25
+ import { isCompositionKey } from "../../utils/isCompositionKey";
25
26
  import { useAui } from "@assistant-ui/store";
26
27
  import { flushTapSync } from "@assistant-ui/tap";
27
28
  import { useComposerInputPluginRegistryOptional } from "./ComposerInputPluginContext";
@@ -118,7 +119,7 @@ export namespace ComposerPrimitiveInput {
118
119
  * keyboard shortcuts, file paste support, and intelligent focus management.
119
120
  * It integrates with the composer context to manage message state and submission.
120
121
  *
121
- * When rendered inside `Unstable_TriggerPopoverRoot` and a popover is open, the
122
+ * When rendered inside `TriggerPopoverRoot` and a popover is open, the
122
123
  * underlying `<textarea>` automatically receives `aria-controls`,
123
124
  * `aria-expanded`, `aria-haspopup`, and `aria-activedescendant` for the
124
125
  * combobox relationship. These computed attributes override user-provided
@@ -200,7 +201,7 @@ export const ComposerPrimitiveInput = forwardRef<
200
201
  if (!textareaRef.current?.contains(e.target as Node)) return;
201
202
 
202
203
  // ignore IME composition events
203
- if (e.isComposing) return;
204
+ if (isCompositionKey(e)) return;
204
205
 
205
206
  // Let registered plugins (mention, slash command, etc.) handle Escape first
206
207
  if (pluginRegistry) {
@@ -222,7 +223,7 @@ export const ComposerPrimitiveInput = forwardRef<
222
223
  if (isDisabled) return;
223
224
 
224
225
  // ignore IME composition events
225
- if (e.nativeEvent.isComposing) return;
226
+ if (isCompositionKey(e.nativeEvent)) return;
226
227
 
227
228
  // Let registered plugins (mention, slash command, etc.) handle keyboard events first
228
229
  if (pluginRegistry) {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useAui, useAuiState } from "@assistant-ui/store";
4
4
  import { useResource } from "@assistant-ui/tap";
5
- import type { Unstable_TriggerAdapter } from "@assistant-ui/core";
5
+ import type { TriggerAdapter } from "@assistant-ui/core";
6
6
  import {
7
7
  createContext,
8
8
  forwardRef,
@@ -74,7 +74,7 @@ export namespace ComposerPrimitiveTriggerPopover {
74
74
  /** Overrides trigger detection for both textarea and Lexical inputs. `endOffset` is the exclusive replace bound. */
75
75
  readonly matcher?: TriggerMatcher | undefined;
76
76
  /** Adapter providing categories and items. */
77
- readonly adapter?: Unstable_TriggerAdapter | undefined;
77
+ readonly adapter?: TriggerAdapter | undefined;
78
78
  /** Whether the adapter is resolving items, surfaced to the popover scope for async sources. @default false */
79
79
  readonly isLoading?: boolean | undefined;
80
80
  };
@@ -89,22 +89,22 @@ export namespace ComposerPrimitiveTriggerPopover {
89
89
  * `<TriggerPopover.Directive>` or `<TriggerPopover.Action>` as a child. Without
90
90
  * a behavior the trigger stays closed.
91
91
  *
92
- * Must be placed inside `ComposerPrimitive.Unstable_TriggerPopoverRoot`.
92
+ * Must be placed inside `ComposerPrimitive.TriggerPopoverRoot`.
93
93
  *
94
94
  * @example
95
95
  * ```tsx
96
- * <ComposerPrimitive.Unstable_TriggerPopover
96
+ * <ComposerPrimitive.TriggerPopover
97
97
  * char="@"
98
98
  * adapter={mentionAdapter}
99
99
  * >
100
- * <ComposerPrimitive.Unstable_TriggerPopover.Directive formatter={formatter} />
101
- * <ComposerPrimitive.Unstable_TriggerPopoverCategories>
100
+ * <ComposerPrimitive.TriggerPopover.Directive formatter={formatter} />
101
+ * <ComposerPrimitive.TriggerPopoverCategories>
102
102
  * {(cats) => cats.map(...)}
103
- * </ComposerPrimitive.Unstable_TriggerPopoverCategories>
104
- * <ComposerPrimitive.Unstable_TriggerPopoverItems>
103
+ * </ComposerPrimitive.TriggerPopoverCategories>
104
+ * <ComposerPrimitive.TriggerPopoverItems>
105
105
  * {(items) => items.map(...)}
106
- * </ComposerPrimitive.Unstable_TriggerPopoverItems>
107
- * </ComposerPrimitive.Unstable_TriggerPopover>
106
+ * </ComposerPrimitive.TriggerPopoverItems>
107
+ * </ComposerPrimitive.TriggerPopover>
108
108
  * ```
109
109
  */
110
110
  export const ComposerPrimitiveTriggerPopover = forwardRef<
@@ -196,7 +196,7 @@ export const ComposerPrimitiveTriggerPopover = forwardRef<
196
196
  ...(behavior ? { behavior } : {}),
197
197
  resource: getResource(),
198
198
  });
199
- }, [root, char, hasMatcher, behavior]);
199
+ }, [root, char, hasMatcher, registeredMatcher, behavior]);
200
200
 
201
201
  const pluginRegistry = useComposerInputPluginRegistryOptional();
202
202
  useEffect(() => {
@@ -1,20 +1,17 @@
1
1
  "use client";
2
2
 
3
- import type {
4
- Unstable_DirectiveFormatter,
5
- Unstable_TriggerItem,
6
- } from "@assistant-ui/core";
7
- import { unstable_defaultDirectiveFormatter } from "@assistant-ui/core";
3
+ import type { DirectiveFormatter, TriggerItem } from "@assistant-ui/core";
4
+ import { defaultDirectiveFormatter } from "@assistant-ui/core";
8
5
  import { useEffect, useInsertionEffect, useRef, type FC } from "react";
9
6
  import { useTriggerBehaviorRegistration } from "./TriggerPopover";
10
7
  import type { TriggerBehavior } from "./triggerSelectionResource";
11
8
 
12
9
  export namespace ComposerPrimitiveTriggerPopoverAction {
13
10
  export type Props = {
14
- /** Defaults to `unstable_defaultDirectiveFormatter`. */
15
- readonly formatter?: Unstable_DirectiveFormatter | undefined;
11
+ /** Defaults to `defaultDirectiveFormatter`. */
12
+ readonly formatter?: DirectiveFormatter | undefined;
16
13
  /** Fires the moment an item is selected; runs regardless of `removeOnExecute`. */
17
- readonly onExecute: (item: Unstable_TriggerItem) => void;
14
+ readonly onExecute: (item: TriggerItem) => void;
18
15
  /** When true, strips the trigger text after executing. Defaults to `false` (keeps audit-trail chip). */
19
16
  readonly removeOnExecute?: boolean | undefined;
20
17
  };
@@ -25,16 +22,16 @@ export namespace ComposerPrimitiveTriggerPopoverAction {
25
22
  * optionally leaving a directive chip behind as an audit trail. Render exactly
26
23
  * one behavior sub-primitive per `<TriggerPopover>`.
27
24
  *
28
- * Exposed as `ComposerPrimitive.Unstable_TriggerPopover.Action`.
25
+ * Exposed as `ComposerPrimitive.TriggerPopover.Action`.
29
26
  *
30
27
  * @example
31
28
  * ```tsx
32
- * <ComposerPrimitive.Unstable_TriggerPopover char="/" adapter={slashAdapter}>
33
- * <ComposerPrimitive.Unstable_TriggerPopover.Action
29
+ * <ComposerPrimitive.TriggerPopover char="/" adapter={slashAdapter}>
30
+ * <ComposerPrimitive.TriggerPopover.Action
34
31
  * onExecute={(item) => commandHandlers[item.id]?.()}
35
32
  * removeOnExecute={false}
36
33
  * />
37
- * </ComposerPrimitive.Unstable_TriggerPopover>
34
+ * </ComposerPrimitive.TriggerPopover>
38
35
  * ```
39
36
  */
40
37
  export const ComposerPrimitiveTriggerPopoverAction: FC<
@@ -49,7 +46,7 @@ export const ComposerPrimitiveTriggerPopoverAction: FC<
49
46
  useEffect(() => {
50
47
  const behavior: TriggerBehavior = {
51
48
  kind: "action",
52
- formatter: formatter ?? unstable_defaultDirectiveFormatter,
49
+ formatter: formatter ?? defaultDirectiveFormatter,
53
50
  onExecute: (item) => onExecuteRef.current(item),
54
51
  ...(removeOnExecute !== undefined ? { removeOnExecute } : {}),
55
52
  };
@@ -10,7 +10,7 @@ import {
10
10
  } from "react";
11
11
  import { composeEventHandlers } from "radix-ui/internal";
12
12
  import { useTriggerPopoverScopeContext } from "./TriggerPopover";
13
- import type { Unstable_TriggerCategory } from "@assistant-ui/core";
13
+ import type { TriggerCategory } from "@assistant-ui/core";
14
14
 
15
15
  export namespace ComposerPrimitiveTriggerPopoverCategories {
16
16
  export type Element = ComponentRef<typeof Primitive.div>;
@@ -18,7 +18,7 @@ export namespace ComposerPrimitiveTriggerPopoverCategories {
18
18
  ComponentPropsWithoutRef<typeof Primitive.div>,
19
19
  "children"
20
20
  > & {
21
- children: (categories: readonly Unstable_TriggerCategory[]) => ReactNode;
21
+ children: (categories: readonly TriggerCategory[]) => ReactNode;
22
22
  };
23
23
  }
24
24
 
@@ -1,20 +1,17 @@
1
1
  "use client";
2
2
 
3
- import type {
4
- Unstable_DirectiveFormatter,
5
- Unstable_TriggerItem,
6
- } from "@assistant-ui/core";
7
- import { unstable_defaultDirectiveFormatter } from "@assistant-ui/core";
3
+ import type { DirectiveFormatter, TriggerItem } from "@assistant-ui/core";
4
+ import { defaultDirectiveFormatter } from "@assistant-ui/core";
8
5
  import { useEffect, useInsertionEffect, useRef, type FC } from "react";
9
6
  import { useTriggerBehaviorRegistration } from "./TriggerPopover";
10
7
  import type { TriggerBehavior } from "./triggerSelectionResource";
11
8
 
12
9
  export namespace ComposerPrimitiveTriggerPopoverDirective {
13
10
  export type Props = {
14
- /** Defaults to `unstable_defaultDirectiveFormatter`. */
15
- readonly formatter?: Unstable_DirectiveFormatter | undefined;
11
+ /** Defaults to `defaultDirectiveFormatter`. */
12
+ readonly formatter?: DirectiveFormatter | undefined;
16
13
  /** Fires after an item has been inserted into the composer. */
17
- readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;
14
+ readonly onInserted?: ((item: TriggerItem) => void) | undefined;
18
15
  };
19
16
  }
20
17
 
@@ -22,16 +19,16 @@ export namespace ComposerPrimitiveTriggerPopoverDirective {
22
19
  * Configures a `<TriggerPopover>` to insert a directive chip when an item is
23
20
  * selected. Render exactly one behavior sub-primitive per `<TriggerPopover>`.
24
21
  *
25
- * Exposed as `ComposerPrimitive.Unstable_TriggerPopover.Directive`.
22
+ * Exposed as `ComposerPrimitive.TriggerPopover.Directive`.
26
23
  *
27
24
  * @example
28
25
  * ```tsx
29
- * <ComposerPrimitive.Unstable_TriggerPopover char="@" adapter={mentionAdapter}>
30
- * <ComposerPrimitive.Unstable_TriggerPopover.Directive
31
- * formatter={unstable_defaultDirectiveFormatter}
26
+ * <ComposerPrimitive.TriggerPopover char="@" adapter={mentionAdapter}>
27
+ * <ComposerPrimitive.TriggerPopover.Directive
28
+ * formatter={defaultDirectiveFormatter}
32
29
  * onInserted={(item) => track("mention", item.id)}
33
30
  * />
34
- * </ComposerPrimitive.Unstable_TriggerPopover>
31
+ * </ComposerPrimitive.TriggerPopover>
35
32
  * ```
36
33
  */
37
34
  export const ComposerPrimitiveTriggerPopoverDirective: FC<
@@ -46,7 +43,7 @@ export const ComposerPrimitiveTriggerPopoverDirective: FC<
46
43
  useEffect(() => {
47
44
  const behavior: TriggerBehavior = {
48
45
  kind: "directive",
49
- formatter: formatter ?? unstable_defaultDirectiveFormatter,
46
+ formatter: formatter ?? defaultDirectiveFormatter,
50
47
  onInserted: (item) => onInsertedRef.current?.(item),
51
48
  };
52
49
  return register(behavior);
@@ -0,0 +1,61 @@
1
+ import { describe, expect, expectTypeOf, it } from "vitest";
2
+ import * as ComposerPrimitive from "../../composer";
3
+ import {
4
+ defaultDirectiveFormatter,
5
+ unstable_defaultDirectiveFormatter,
6
+ type DirectiveFormatter,
7
+ type TriggerAdapter,
8
+ type TriggerCategory,
9
+ type TriggerItem,
10
+ type Unstable_DirectiveFormatter,
11
+ type Unstable_TriggerBehavior,
12
+ type Unstable_TriggerItem,
13
+ } from "../../../index";
14
+ import type { TriggerBehavior } from "./triggerSelectionResource";
15
+
16
+ describe("stable trigger popover exports", () => {
17
+ it("keeps every old composer member as an alias of its stable member", () => {
18
+ expect(ComposerPrimitive.Unstable_TriggerPopover).toBe(
19
+ ComposerPrimitive.TriggerPopover,
20
+ );
21
+ expect(ComposerPrimitive.Unstable_TriggerPopover.Directive).toBe(
22
+ ComposerPrimitive.TriggerPopover.Directive,
23
+ );
24
+ expect(ComposerPrimitive.Unstable_TriggerPopover.Action).toBe(
25
+ ComposerPrimitive.TriggerPopover.Action,
26
+ );
27
+ expect(ComposerPrimitive.Unstable_TriggerPopoverRoot).toBe(
28
+ ComposerPrimitive.TriggerPopoverRoot,
29
+ );
30
+ expect(ComposerPrimitive.Unstable_TriggerPopoverCategories).toBe(
31
+ ComposerPrimitive.TriggerPopoverCategories,
32
+ );
33
+ expect(ComposerPrimitive.Unstable_TriggerPopoverCategoryItem).toBe(
34
+ ComposerPrimitive.TriggerPopoverCategoryItem,
35
+ );
36
+ expect(ComposerPrimitive.Unstable_TriggerPopoverItems).toBe(
37
+ ComposerPrimitive.TriggerPopoverItems,
38
+ );
39
+ expect(ComposerPrimitive.Unstable_TriggerPopoverItem).toBe(
40
+ ComposerPrimitive.TriggerPopoverItem,
41
+ );
42
+ expect(ComposerPrimitive.Unstable_TriggerPopoverBack).toBe(
43
+ ComposerPrimitive.TriggerPopoverBack,
44
+ );
45
+ });
46
+
47
+ it("keeps the old members' namespaced prop types", () => {
48
+ expectTypeOf<ComposerPrimitive.Unstable_TriggerPopoverItem.Props>().toEqualTypeOf<ComposerPrimitive.TriggerPopoverItem.Props>();
49
+ expectTypeOf<ComposerPrimitive.Unstable_TriggerPopoverBack.Element>().toEqualTypeOf<ComposerPrimitive.TriggerPopoverBack.Element>();
50
+ });
51
+
52
+ it("exports the stable adapter, item, category and formatter names beside the old ones", () => {
53
+ expect(unstable_defaultDirectiveFormatter).toBe(defaultDirectiveFormatter);
54
+ expectTypeOf<Unstable_TriggerItem>().toEqualTypeOf<TriggerItem>();
55
+ expectTypeOf<Unstable_DirectiveFormatter>().toEqualTypeOf<DirectiveFormatter>();
56
+ expectTypeOf<TriggerAdapter["categories"]>().returns.toEqualTypeOf<
57
+ readonly TriggerCategory[]
58
+ >();
59
+ expectTypeOf<Unstable_TriggerBehavior>().toEqualTypeOf<TriggerBehavior>();
60
+ });
61
+ });
@@ -10,7 +10,7 @@ import {
10
10
  } from "react";
11
11
  import { composeEventHandlers } from "radix-ui/internal";
12
12
  import { useTriggerPopoverScopeContext } from "./TriggerPopover";
13
- import type { Unstable_TriggerItem } from "@assistant-ui/core";
13
+ import type { TriggerItem } from "@assistant-ui/core";
14
14
 
15
15
  export namespace ComposerPrimitiveTriggerPopoverItems {
16
16
  export type Element = ComponentRef<typeof Primitive.div>;
@@ -18,7 +18,7 @@ export namespace ComposerPrimitiveTriggerPopoverItems {
18
18
  ComponentPropsWithoutRef<typeof Primitive.div>,
19
19
  "children"
20
20
  > & {
21
- children: (items: readonly Unstable_TriggerItem[]) => ReactNode;
21
+ children: (items: readonly TriggerItem[]) => ReactNode;
22
22
  };
23
23
  }
24
24
 
@@ -53,7 +53,7 @@ ComposerPrimitiveTriggerPopoverItems.displayName =
53
53
  export namespace ComposerPrimitiveTriggerPopoverItem {
54
54
  export type Element = ComponentRef<typeof Primitive.button>;
55
55
  export type Props = ComponentPropsWithoutRef<typeof Primitive.button> & {
56
- item: Unstable_TriggerItem;
56
+ item: TriggerItem;
57
57
  index?: number | undefined;
58
58
  };
59
59
  }
@@ -294,21 +294,21 @@ const TriggerPopoverRootInner: FC<
294
294
  *
295
295
  * @example
296
296
  * ```tsx
297
- * <ComposerPrimitive.Unstable_TriggerPopoverRoot>
298
- * <ComposerPrimitive.Unstable_TriggerPopover char="@" adapter={mentionAdapter}>
299
- * <ComposerPrimitive.Unstable_TriggerPopover.Directive formatter={formatter} />
297
+ * <ComposerPrimitive.TriggerPopoverRoot>
298
+ * <ComposerPrimitive.TriggerPopover char="@" adapter={mentionAdapter}>
299
+ * <ComposerPrimitive.TriggerPopover.Directive formatter={formatter} />
300
300
  * ...
301
- * </ComposerPrimitive.Unstable_TriggerPopover>
301
+ * </ComposerPrimitive.TriggerPopover>
302
302
  *
303
- * <ComposerPrimitive.Unstable_TriggerPopover char="/" adapter={slashAdapter}>
304
- * <ComposerPrimitive.Unstable_TriggerPopover.Action onExecute={handler} />
303
+ * <ComposerPrimitive.TriggerPopover char="/" adapter={slashAdapter}>
304
+ * <ComposerPrimitive.TriggerPopover.Action onExecute={handler} />
305
305
  * ...
306
- * </ComposerPrimitive.Unstable_TriggerPopover>
306
+ * </ComposerPrimitive.TriggerPopover>
307
307
  *
308
308
  * <ComposerPrimitive.Root>
309
309
  * <ComposerPrimitive.Input />
310
310
  * </ComposerPrimitive.Root>
311
- * </ComposerPrimitive.Unstable_TriggerPopoverRoot>
311
+ * </ComposerPrimitive.TriggerPopoverRoot>
312
312
  * ```
313
313
  */
314
314
  export const ComposerPrimitiveTriggerPopoverRoot: FC<
@@ -1,3 +1,5 @@
1
+ import type { TriggerMatch, TriggerMatcher } from "./composer/trigger";
2
+
1
3
  export { ComposerPrimitiveRoot as Root } from "./composer/ComposerRoot";
2
4
  export { ComposerPrimitiveInput as Input } from "./composer/ComposerInput";
3
5
  export { ComposerPrimitiveSend as Send } from "./composer/ComposerSend";
@@ -15,12 +17,26 @@ export { ComposerPrimitiveQuoteText as QuoteText } from "./composer/ComposerQuot
15
17
  export { ComposerPrimitiveQuoteDismiss as QuoteDismiss } from "./composer/ComposerQuote";
16
18
  export { ComposerPrimitiveQueue as Queue } from "./composer/ComposerQueue";
17
19
 
20
+ export { ComposerPrimitiveTriggerPopover as TriggerPopover } from "./composer/trigger";
21
+ /** @deprecated Use `ComposerPrimitive.TriggerPopover` instead. */
18
22
  export { ComposerPrimitiveTriggerPopover as Unstable_TriggerPopover } from "./composer/trigger";
23
+ export { ComposerPrimitiveTriggerPopoverRoot as TriggerPopoverRoot } from "./composer/trigger";
24
+ /** @deprecated Use `ComposerPrimitive.TriggerPopoverRoot` instead. */
19
25
  export { ComposerPrimitiveTriggerPopoverRoot as Unstable_TriggerPopoverRoot } from "./composer/trigger";
26
+ export { ComposerPrimitiveTriggerPopoverCategories as TriggerPopoverCategories } from "./composer/trigger";
27
+ /** @deprecated Use `ComposerPrimitive.TriggerPopoverCategories` instead. */
20
28
  export { ComposerPrimitiveTriggerPopoverCategories as Unstable_TriggerPopoverCategories } from "./composer/trigger";
29
+ export { ComposerPrimitiveTriggerPopoverCategoryItem as TriggerPopoverCategoryItem } from "./composer/trigger";
30
+ /** @deprecated Use `ComposerPrimitive.TriggerPopoverCategoryItem` instead. */
21
31
  export { ComposerPrimitiveTriggerPopoverCategoryItem as Unstable_TriggerPopoverCategoryItem } from "./composer/trigger";
32
+ export { ComposerPrimitiveTriggerPopoverItems as TriggerPopoverItems } from "./composer/trigger";
33
+ /** @deprecated Use `ComposerPrimitive.TriggerPopoverItems` instead. */
22
34
  export { ComposerPrimitiveTriggerPopoverItems as Unstable_TriggerPopoverItems } from "./composer/trigger";
35
+ export { ComposerPrimitiveTriggerPopoverItem as TriggerPopoverItem } from "./composer/trigger";
36
+ /** @deprecated Use `ComposerPrimitive.TriggerPopoverItem` instead. */
23
37
  export { ComposerPrimitiveTriggerPopoverItem as Unstable_TriggerPopoverItem } from "./composer/trigger";
38
+ export { ComposerPrimitiveTriggerPopoverBack as TriggerPopoverBack } from "./composer/trigger";
39
+ /** @deprecated Use `ComposerPrimitive.TriggerPopoverBack` instead. */
24
40
  export { ComposerPrimitiveTriggerPopoverBack as Unstable_TriggerPopoverBack } from "./composer/trigger";
25
41
  export { useTriggerPopoverRootContext as unstable_useTriggerPopoverRootContext } from "./composer/trigger";
26
42
  export { useTriggerPopoverRootContextOptional as unstable_useTriggerPopoverRootContextOptional } from "./composer/trigger";
@@ -29,4 +45,8 @@ export { useTriggerPopoverScopeContextOptional as unstable_useTriggerPopoverScop
29
45
  export { useTriggerPopoverTriggers as unstable_useTriggerPopoverTriggers } from "./composer/trigger";
30
46
  export { useTriggerPopoverTriggersOptional as unstable_useTriggerPopoverTriggersOptional } from "./composer/trigger";
31
47
  export type { RegisteredTrigger as Unstable_RegisteredTrigger } from "./composer/trigger";
32
- export type { TriggerMatcher as Unstable_TriggerMatcher } from "./composer/trigger";
48
+ export type { TriggerMatch, TriggerMatcher } from "./composer/trigger";
49
+ /** @deprecated Use `TriggerMatcher` instead. */
50
+ export type Unstable_TriggerMatcher = TriggerMatcher;
51
+ /** @deprecated Use `TriggerMatch` instead. */
52
+ export type Unstable_TriggerMatch = TriggerMatch;