@assistant-ui/react 0.14.27 → 0.14.28

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 (180) hide show
  1. package/dist/client/ExternalThread.d.ts.map +1 -1
  2. package/dist/client/ExternalThread.js +2 -2
  3. package/dist/client/ExternalThread.js.map +1 -1
  4. package/dist/context/react/utils/createContextStoreHook.d.ts.map +1 -1
  5. package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +1 -1
  6. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  7. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts.map +1 -1
  8. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  9. package/dist/model-context/makeAssistantVisible.d.ts.map +1 -1
  10. package/dist/model-context/makeAssistantVisible.js +7 -1
  11. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  12. package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -1
  13. package/dist/primitives/actionBar/ActionBarEdit.d.ts.map +1 -1
  14. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -1
  15. package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts.map +1 -1
  16. package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts.map +1 -1
  17. package/dist/primitives/actionBar/ActionBarReload.d.ts.map +1 -1
  18. package/dist/primitives/actionBar/ActionBarRoot.d.ts.map +1 -1
  19. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
  20. package/dist/primitives/actionBar/ActionBarSpeak.d.ts.map +1 -1
  21. package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -1
  22. package/dist/primitives/actionBarMore/ActionBarMoreContent.d.ts.map +1 -1
  23. package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
  24. package/dist/primitives/actionBarMore/ActionBarMoreItem.d.ts.map +1 -1
  25. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.d.ts.map +1 -1
  26. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.d.ts.map +1 -1
  27. package/dist/primitives/assistantModal/AssistantModalAnchor.d.ts.map +1 -1
  28. package/dist/primitives/assistantModal/AssistantModalContent.d.ts.map +1 -1
  29. package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
  30. package/dist/primitives/assistantModal/AssistantModalTrigger.d.ts.map +1 -1
  31. package/dist/primitives/assistantModal/popoverRenderPrimitives.d.ts.map +1 -1
  32. package/dist/primitives/attachment/AttachmentRemove.d.ts.map +1 -1
  33. package/dist/primitives/attachment/AttachmentRoot.d.ts.map +1 -1
  34. package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -1
  35. package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
  36. package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -1
  37. package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -1
  38. package/dist/primitives/branchPicker/BranchPickerRoot.d.ts.map +1 -1
  39. package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
  40. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts.map +1 -1
  41. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts.map +1 -1
  42. package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
  43. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
  44. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  45. package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
  46. package/dist/primitives/composer/ComposerDictate.d.ts.map +1 -1
  47. package/dist/primitives/composer/ComposerDictationTranscript.d.ts.map +1 -1
  48. package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
  49. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  50. package/dist/primitives/composer/ComposerQuote.d.ts.map +1 -1
  51. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  52. package/dist/primitives/composer/ComposerRoot.d.ts +5 -0
  53. package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
  54. package/dist/primitives/composer/ComposerRoot.js +86 -40
  55. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  56. package/dist/primitives/composer/ComposerSend.d.ts.map +1 -1
  57. package/dist/primitives/composer/ComposerStopDictation.d.ts.map +1 -1
  58. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
  59. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  60. package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts.map +1 -1
  61. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -1
  62. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
  63. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -1
  64. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
  65. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
  66. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
  67. package/dist/primitives/composer/trigger/detectTrigger.d.ts +1 -1
  68. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
  69. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
  70. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
  71. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  72. package/dist/primitives/dropdownMenuRenderPrimitives.d.ts.map +1 -1
  73. package/dist/primitives/error/ErrorMessage.d.ts.map +1 -1
  74. package/dist/primitives/error/ErrorMessage.js.map +1 -1
  75. package/dist/primitives/error/ErrorRoot.d.ts.map +1 -1
  76. package/dist/primitives/message/MessageParts.js.map +1 -1
  77. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  78. package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
  79. package/dist/primitives/messagePart/MessagePartImage.d.ts.map +1 -1
  80. package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
  81. package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
  82. package/dist/primitives/messagePart/useMessagePartData.d.ts +3 -2
  83. package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -1
  84. package/dist/primitives/messagePart/useMessagePartData.js +3 -2
  85. package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
  86. package/dist/primitives/messagePart/useMessagePartFile.d.ts +5 -4
  87. package/dist/primitives/messagePart/useMessagePartFile.d.ts.map +1 -1
  88. package/dist/primitives/messagePart/useMessagePartFile.js +11 -3
  89. package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -1
  90. package/dist/primitives/messagePart/useMessagePartImage.d.ts +5 -4
  91. package/dist/primitives/messagePart/useMessagePartImage.d.ts.map +1 -1
  92. package/dist/primitives/messagePart/useMessagePartImage.js +10 -3
  93. package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -1
  94. package/dist/primitives/messagePart/useMessagePartReasoning.d.ts +5 -4
  95. package/dist/primitives/messagePart/useMessagePartReasoning.d.ts.map +1 -1
  96. package/dist/primitives/messagePart/useMessagePartReasoning.js +10 -3
  97. package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -1
  98. package/dist/primitives/messagePart/useMessagePartSource.d.ts +6 -4
  99. package/dist/primitives/messagePart/useMessagePartSource.d.ts.map +1 -1
  100. package/dist/primitives/messagePart/useMessagePartSource.js +12 -3
  101. package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -1
  102. package/dist/primitives/messagePart/useMessagePartText.d.ts +6 -5
  103. package/dist/primitives/messagePart/useMessagePartText.d.ts.map +1 -1
  104. package/dist/primitives/messagePart/useMessagePartText.js +10 -3
  105. package/dist/primitives/messagePart/useMessagePartText.js.map +1 -1
  106. package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -1
  107. package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -1
  108. package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -1
  109. package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
  110. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts.map +1 -1
  111. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
  112. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  113. package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -1
  114. package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
  115. package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -1
  116. package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
  117. package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -1
  118. package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
  119. package/dist/primitives/thread/ThreadScrollToBottom.d.ts.map +1 -1
  120. package/dist/primitives/thread/ThreadSuggestion.d.ts.map +1 -1
  121. package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
  122. package/dist/primitives/thread/ThreadViewport.js +53 -19
  123. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  124. package/dist/primitives/thread/ThreadViewportFooter.d.ts.map +1 -1
  125. package/dist/primitives/thread/topAnchor/createReserveObservers.d.ts.map +1 -1
  126. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts +4 -0
  127. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts.map +1 -1
  128. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js +14 -1
  129. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -1
  130. package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts +13 -1
  131. package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts.map +1 -1
  132. package/dist/primitives/thread/topAnchor/topAnchorTurn.js +14 -1
  133. package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
  134. package/dist/primitives/thread/useThreadViewportAutoScroll.js +2 -1
  135. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  136. package/dist/primitives/threadList/ThreadListLoadMore.d.ts.map +1 -1
  137. package/dist/primitives/threadList/ThreadListNew.d.ts.map +1 -1
  138. package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -1
  139. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
  140. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
  141. package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts.map +1 -1
  142. package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts.map +1 -1
  143. package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -1
  144. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  145. package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -1
  146. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  147. package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -1
  148. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -1
  149. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
  150. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.d.ts.map +1 -1
  151. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
  152. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.d.ts.map +1 -1
  153. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -1
  154. package/dist/tests/setup.d.ts +1 -1
  155. package/dist/utils/Primitive.d.ts.map +1 -1
  156. package/dist/utils/Primitive.js.map +1 -1
  157. package/dist/utils/createActionButton.d.ts.map +1 -1
  158. package/package.json +20 -20
  159. package/src/client/ExternalThread.ts +2 -1
  160. package/src/model-context/makeAssistantVisible.test.tsx +146 -0
  161. package/src/model-context/makeAssistantVisible.tsx +18 -1
  162. package/src/primitives/composer/ComposerRoot.test.tsx +226 -5
  163. package/src/primitives/composer/ComposerRoot.tsx +50 -1
  164. package/src/primitives/messagePart/useMessagePartData.ts +3 -2
  165. package/src/primitives/messagePart/useMessagePartFile.ts +19 -7
  166. package/src/primitives/messagePart/useMessagePartImage.ts +18 -7
  167. package/src/primitives/messagePart/useMessagePartReasoning.ts +15 -6
  168. package/src/primitives/messagePart/useMessagePartSource.ts +20 -7
  169. package/src/primitives/messagePart/useMessagePartText.ts +15 -5
  170. package/src/primitives/thread/ThreadViewport.tsx +15 -0
  171. package/src/primitives/thread/topAnchor/mountTopAnchorReserve.test.ts +243 -0
  172. package/src/primitives/thread/topAnchor/mountTopAnchorReserve.ts +28 -7
  173. package/src/primitives/thread/topAnchor/topAnchorTurn.test.ts +69 -0
  174. package/src/primitives/thread/topAnchor/topAnchorTurn.ts +29 -0
  175. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +86 -1
  176. package/src/primitives/thread/useThreadViewportAutoScroll.ts +6 -1
  177. package/src/tests/BaseComposerRuntimeCore.test.ts +12 -3
  178. package/src/tests/external-thread-attachments.test.tsx +81 -0
  179. package/src/tests/messagePartSwitchRace.test.tsx +158 -0
  180. package/src/tests/messagePartTypeMismatch.test.tsx +146 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreRoot.js","names":["c","_c","createContext","Dispatch","FC","SetStateAction","useContext","DropdownMenu","DropdownMenuPrimitive","useControllableState","ScopedProps","useDropdownMenuScope","ThreadListItemMoreSetOpenContext","useThreadListItemMoreSetOpen","ThreadListItemMoreSharedFocusGroupContext","useThreadListItemMoreSharedFocusGroup","ThreadListItemMorePrimitiveRoot","Props","DropdownMenuProps","sharedFocusGroup","t0","$","__scopeThreadListItemMore","defaultOpen","modalProp","onOpenChange","openProp","rest","t1","open","modal","undefined","scope","t2","t3","onChange","t4","prop","defaultProp","caller","setOpen","t5","t6","t7","displayName"],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type Dispatch,\n type FC,\n type SetStateAction,\n useContext,\n} from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { type ScopedProps, useDropdownMenuScope } from \"./scope\";\n\nconst ThreadListItemMoreSetOpenContext = createContext<\n Dispatch<SetStateAction<boolean>>\n>(() => {});\n\nexport const useThreadListItemMoreSetOpen = (): Dispatch<\n SetStateAction<boolean>\n> => useContext(ThreadListItemMoreSetOpenContext);\n\nconst ThreadListItemMoreSharedFocusGroupContext = createContext(false);\n\nexport const useThreadListItemMoreSharedFocusGroup = (): boolean =>\n useContext(ThreadListItemMoreSharedFocusGroupContext);\n\nexport namespace ThreadListItemMorePrimitiveRoot {\n export type Props = DropdownMenuPrimitive.DropdownMenuProps & {\n /**\n * Join the menu to the thread list's focus group: Right opens it, Left/Escape\n * close it and return focus to the trigger (mirrored in RTL). Forces the menu\n * non-modal, since a focus trap can't let focus cross the trigger/menu\n * boundary. Defaults to a standalone modal dropdown.\n */\n sharedFocusGroup?: boolean | undefined;\n };\n}\n\n/**\n * Root container for the overflow menu, built on Radix DropdownMenu.\n *\n * Defaults to a standard, self-contained modal dropdown. Pass\n * {@link ThreadListItemMorePrimitiveRoot.Props.sharedFocusGroup} to fold it into\n * the thread list's keyboard navigation instead.\n */\nexport const ThreadListItemMorePrimitiveRoot: FC<\n ThreadListItemMorePrimitiveRoot.Props\n> = ({\n __scopeThreadListItemMore,\n open: openProp,\n defaultOpen,\n onOpenChange,\n sharedFocusGroup = false,\n modal: modalProp,\n ...rest\n}: ScopedProps<ThreadListItemMorePrimitiveRoot.Props>) => {\n const modal = sharedFocusGroup ? false : (modalProp ?? true);\n const scope = useDropdownMenuScope(__scopeThreadListItemMore);\n const [open, setOpen] = useControllableState({\n prop: openProp,\n defaultProp: defaultOpen ?? false,\n caller: \"ThreadListItemMorePrimitive.Root\",\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n\n return (\n <ThreadListItemMoreSharedFocusGroupContext.Provider\n value={sharedFocusGroup}\n >\n <ThreadListItemMoreSetOpenContext.Provider value={setOpen}>\n <DropdownMenuPrimitive.Root\n {...scope}\n {...rest}\n modal={modal}\n open={open}\n onOpenChange={setOpen}\n />\n </ThreadListItemMoreSetOpenContext.Provider>\n </ThreadListItemMoreSharedFocusGroupContext.Provider>\n );\n};\n\nThreadListItemMorePrimitiveRoot.displayName =\n \"ThreadListItemMorePrimitive.Root\";\n"],"mappings":";;;;;;;;AAaA,MAAMY,mCAAmCV,oBAEjC,CAAC,CAAC;AAEV,MAAaW,qCAA+B;CAAA,OAEvCP,WAAWM,gCAAgC;AAAC;AAEjD,MAAME,4CAA4CZ,cAAc,KAAK;AAErE,MAAaa,8CAAwC;CAAA,OACnDT,WAAWQ,yCAAyC;AAAC;;;;;;;;AAqBvD,MAAaE,mCAETI,OAAA;CAAA,MAAAC,IAAApB,EAAA,EAAA;CAAA,IAAAqB;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAP,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAE,2BAAAO,MAAAH,UAAAH,aAAAE,cAAAN,kBAAAS,IAAAE,OAAAN,cAAAG,QAAAP;EAQgDC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;EAAAL,EAAA,KAAAM;EAAAN,EAAA,KAAAO;CAAA,OAAA;EAAAN,4BAAAD,EAAA;EAAAE,cAAAF,EAAA;EAAAG,YAAAH,EAAA;EAAAI,eAAAJ,EAAA;EAAAK,WAAAL,EAAA;EAAAM,OAAAN,EAAA;EAAAO,KAAAP,EAAA;CAAA;CAHnD,MAAAF,mBAAAS,OAAAG,KAAAA,IAAA,QAAAH;CAIA,MAAAE,QAAcX,mBAAA,QAA4BK,aAAA;CAC1C,MAAAQ,QAAcrB,qBAAqBW,yBAAyB;CAG7C,MAAAW,KAAAV,eAAA;CAAoB,IAAAW;CAAA,IAAAb,EAAA,OAAAI,cAAA;EAE7BS,KAAAT,eAAA,EAAAU,UAA2BV,aAAkB,IAA7C,CAA6C;EAACJ,EAAA,KAAAI;EAAAJ,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAAA,IAAAe;CAAA,IAAAf,EAAA,QAAAK,YAAAL,EAAA,QAAAY,MAAAZ,EAAA,QAAAa,IAAA;EAJPE,KAAA;GAAAC,MACrCX;GAAQY,aACDL;GAAoBM,QACzB;GAAkC,GACtCL;EACN;EAACb,EAAA,MAAAK;EAAAL,EAAA,MAAAY;EAAAZ,EAAA,MAAAa;EAAAb,EAAA,MAAAe;CAAA,OAAAA,KAAAf,EAAA;CALD,MAAA,CAAAQ,MAAAW,WAAwB/B,qBAAqB2B,EAK5C;CAAE,IAAAK;CAAA,IAAApB,EAAA,QAAAS,SAAAT,EAAA,QAAAQ,QAAAR,EAAA,QAAAM,QAAAN,EAAA,QAAAW,SAAAX,EAAA,QAAAmB,SAAA;EAOGC,KAAA,oBAAA,aAAA,MAAA;GAAA,GACMT;GAAK,GACLL;GACGG;GACDD;GACQW,cAAAA;EAAO,CAAA;EACrBnB,EAAA,MAAAS;EAAAT,EAAA,MAAAQ;EAAAR,EAAA,MAAAM;EAAAN,EAAA,MAAAW;EAAAX,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoB;CAAA,OAAAA,KAAApB,EAAA;CAAA,IAAAqB;CAAA,IAAArB,EAAA,QAAAmB,WAAAnB,EAAA,QAAAoB,IAAA;EAPJC,KAAA,oBAAA,iCAAA,UAAA;GAAkDF,OAAAA;aAChDC;EAOF,CAAA;EAA4CpB,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoB;EAAApB,EAAA,MAAAqB;CAAA,OAAAA,KAAArB,EAAA;CAAA,IAAAsB;CAAA,IAAAtB,EAAA,QAAAF,oBAAAE,EAAA,QAAAqB,IAAA;EAX9CC,KAAA,oBAAA,0CAAA,UAAA;GACSxB,OAAAA;aAEPuB;EASF,CAAA;EAAqDrB,EAAA,MAAAF;EAAAE,EAAA,MAAAqB;EAAArB,EAAA,MAAAsB;CAAA,OAAAA,KAAAtB,EAAA;CAAA,OAZrDsB;AAYqD;AAIzD3B,gCAAgC4B,cAC9B"}
1
+ {"version":3,"file":"ThreadListItemMoreRoot.js","names":["c","_c","createContext","Dispatch","FC","SetStateAction","useContext","DropdownMenu","DropdownMenuPrimitive","useControllableState","ScopedProps","useDropdownMenuScope","ThreadListItemMoreSetOpenContext","useThreadListItemMoreSetOpen","ThreadListItemMoreSharedFocusGroupContext","useThreadListItemMoreSharedFocusGroup","ThreadListItemMorePrimitiveRoot","Props","DropdownMenuProps","sharedFocusGroup","t0","$","__scopeThreadListItemMore","defaultOpen","modalProp","onOpenChange","openProp","rest","t1","open","modal","undefined","scope","t2","t3","onChange","t4","prop","defaultProp","caller","setOpen","t5","t6","t7","displayName"],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n type Dispatch,\n type FC,\n type SetStateAction,\n useContext,\n} from \"react\";\nimport { DropdownMenu as DropdownMenuPrimitive } from \"radix-ui\";\nimport { useControllableState } from \"@radix-ui/react-use-controllable-state\";\nimport { type ScopedProps, useDropdownMenuScope } from \"./scope\";\n\nconst ThreadListItemMoreSetOpenContext = createContext<\n Dispatch<SetStateAction<boolean>>\n>(() => {});\n\nexport const useThreadListItemMoreSetOpen = (): Dispatch<\n SetStateAction<boolean>\n> => useContext(ThreadListItemMoreSetOpenContext);\n\nconst ThreadListItemMoreSharedFocusGroupContext = createContext(false);\n\nexport const useThreadListItemMoreSharedFocusGroup = (): boolean =>\n useContext(ThreadListItemMoreSharedFocusGroupContext);\n\nexport namespace ThreadListItemMorePrimitiveRoot {\n export type Props = DropdownMenuPrimitive.DropdownMenuProps & {\n /**\n * Join the menu to the thread list's focus group: Right opens it, Left/Escape\n * close it and return focus to the trigger (mirrored in RTL). Forces the menu\n * non-modal, since a focus trap can't let focus cross the trigger/menu\n * boundary. Defaults to a standalone modal dropdown.\n */\n sharedFocusGroup?: boolean | undefined;\n };\n}\n\n/**\n * Root container for the overflow menu, built on Radix DropdownMenu.\n *\n * Defaults to a standard, self-contained modal dropdown. Pass\n * {@link ThreadListItemMorePrimitiveRoot.Props.sharedFocusGroup} to fold it into\n * the thread list's keyboard navigation instead.\n */\nexport const ThreadListItemMorePrimitiveRoot: FC<\n ThreadListItemMorePrimitiveRoot.Props\n> = ({\n __scopeThreadListItemMore,\n open: openProp,\n defaultOpen,\n onOpenChange,\n sharedFocusGroup = false,\n modal: modalProp,\n ...rest\n}: ScopedProps<ThreadListItemMorePrimitiveRoot.Props>) => {\n const modal = sharedFocusGroup ? false : (modalProp ?? true);\n const scope = useDropdownMenuScope(__scopeThreadListItemMore);\n const [open, setOpen] = useControllableState({\n prop: openProp,\n defaultProp: defaultOpen ?? false,\n caller: \"ThreadListItemMorePrimitive.Root\",\n ...(onOpenChange ? { onChange: onOpenChange } : {}),\n });\n\n return (\n <ThreadListItemMoreSharedFocusGroupContext.Provider\n value={sharedFocusGroup}\n >\n <ThreadListItemMoreSetOpenContext.Provider value={setOpen}>\n <DropdownMenuPrimitive.Root\n {...scope}\n {...rest}\n modal={modal}\n open={open}\n onOpenChange={setOpen}\n />\n </ThreadListItemMoreSetOpenContext.Provider>\n </ThreadListItemMoreSharedFocusGroupContext.Provider>\n );\n};\n\nThreadListItemMorePrimitiveRoot.displayName =\n \"ThreadListItemMorePrimitive.Root\";\n"],"mappings":";;;;;;;;AAaA,MAAMY,mCAAmCV,oBAEjC,CAAC,CAAC;AAEV,MAAaW,qCAA+B;CAAA,OAEvCP,WAAWM,gCAAgC;AAAC;AAEjD,MAAME,4CAA4CZ,cAAc,KAAK;AAErE,MAAaa,8CAAwC;CAAA,OACnDT,WAAWQ,yCAAyC;AAAC;;;;;;;;AAqBvD,MAAaE,mCAETI,OAAA;CAAA,MAAAC,IAAApB,EAAA,EAAA;CAAA,IAAAqB;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAP,EAAA,OAAAD,IAAA;EAAC,CAAA,CAAAE,2BAAAO,MAAAH,UAAAH,aAAAE,cAAAN,kBAAAS,IAAAE,OAAAN,cAAAG,QAAAP;EAQgDC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAG;EAAAH,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;EAAAL,EAAA,KAAAM;EAAAN,EAAA,KAAAO;CAAA,OAAA;EAAAN,4BAAAD,EAAA;EAAAE,cAAAF,EAAA;EAAAG,YAAAH,EAAA;EAAAI,eAAAJ,EAAA;EAAAK,WAAAL,EAAA;EAAAM,OAAAN,EAAA;EAAAO,KAAAP,EAAA;CAAA;CAHnD,MAAAF,mBAAAS,OAAAG,KAAAA,IAAA,QAAAH;CAIA,MAAAE,QAAcX,mBAAA,QAA4BK,aAAA;CAC1C,MAAAQ,QAAcrB,qBAAqBW,yBAAyB;CAG7C,MAAAW,KAAAV,eAAA;CAAoB,IAAAW;CAAA,IAAAb,EAAA,OAAAI,cAAA;EAE7BS,KAAAT,eAAA,EAAAU,UAA2BV,aAAkB,IAA7C,CAA6C;EAACJ,EAAA,KAAAI;EAAAJ,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAAA,IAAAe;CAAA,IAAAf,EAAA,QAAAK,YAAAL,EAAA,QAAAY,MAAAZ,EAAA,QAAAa,IAAA;EAJPE,KAAA;GAAAC,MACrCX;GAAQY,aACDL;GAAoBM,QACzB;GAAkC,GACtCL;EACN;EAACb,EAAA,MAAAK;EAAAL,EAAA,MAAAY;EAAAZ,EAAA,MAAAa;EAAAb,EAAA,MAAAe;CAAA,OAAAA,KAAAf,EAAA;CALD,MAAA,CAAAQ,MAAAW,WAAwB/B,qBAAqB2B,EAK5C;CAAE,IAAAK;CAAA,IAAApB,EAAA,QAAAS,SAAAT,EAAA,QAAAQ,QAAAR,EAAA,QAAAM,QAAAN,EAAA,QAAAW,SAAAX,EAAA,QAAAmB,SAAA;EAOGC,KAAA,oBAAA,aAAA,MAAA;GAAA,GACMT;GAAK,GACLL;GACGG;GACDD;GACQW,cAAAA;EAAO,CAAA;EACrBnB,EAAA,MAAAS;EAAAT,EAAA,MAAAQ;EAAAR,EAAA,MAAAM;EAAAN,EAAA,MAAAW;EAAAX,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoB;CAAA,OAAAA,KAAApB,EAAA;CAAA,IAAAqB;CAAA,IAAArB,EAAA,QAAAmB,WAAAnB,EAAA,QAAAoB,IAAA;EAPJC,KAAA,oBAAA,iCAAA,UAAA;GAAkDF,OAAAA;GAChDC,UAAAA;EAOF,CAAA;EAA4CpB,EAAA,MAAAmB;EAAAnB,EAAA,MAAAoB;EAAApB,EAAA,MAAAqB;CAAA,OAAAA,KAAArB,EAAA;CAAA,IAAAsB;CAAA,IAAAtB,EAAA,QAAAF,oBAAAE,EAAA,QAAAqB,IAAA;EAX9CC,KAAA,oBAAA,0CAAA,UAAA;GACSxB,OAAAA;GAEPuB,UAAAA;EASF,CAAA;EAAqDrB,EAAA,MAAAF;EAAAE,EAAA,MAAAqB;EAAArB,EAAA,MAAAsB;CAAA,OAAAA,KAAAtB,EAAA;CAAA,OAZrDsB;AAYqD;AAIzD3B,gCAAgC4B,cAC9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreSeparator.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreSeparator.tsx"],"mappings":";;;;kBAQiB;OACH,UAAU,oBAAoB,aAAsB;OACpD,QAAQ,2BACX,aAAsB;;cAIpB,sDAAoC,0BAAA,KAAA,aAAA,6CAAA,cAAA"}
1
+ {"version":3,"file":"ThreadListItemMoreSeparator.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreSeparator.tsx"],"mappings":";;;;kBAQiB;OACH,UAAU,oBAAoB,aAAsB;OACpD,QAAQ,2BACX,aAAsB;;cAIpB,sDAAoC,0BAAA,KAAA,aAAA,6CAAA,cAAA;;oBAehD,cAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadListItemMoreTrigger.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx"],"mappings":";;;;kBAkBiB;OACH,UAAU,oBAAoB,aAAsB;OACpD,QAAQ,2BAA2B,aAAsB;;cAG1D,oDAAkC,0BAAA,KAAA,aAAA,2CAAA,cAAA"}
1
+ {"version":3,"file":"ThreadListItemMoreTrigger.d.ts","names":[],"sources":["../../../src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx"],"mappings":";;;;kBAkBiB;OACH,UAAU,oBAAoB,aAAsB;OACpD,QAAQ,2BAA2B,aAAsB;;cAG1D,oDAAkC,0BAAA,KAAA,aAAA,2CAAA,cAAA;;oBAgC9C,cAAA"}
@@ -1 +1 @@
1
- export { };
1
+ export {}
@@ -1 +1 @@
1
- {"version":3,"file":"Primitive.d.ts","names":[],"sources":["../../src/utils/Primitive.tsx"],"mappings":";;;;;;;;;;;;cA0BM;KAmBD,wBAAwB;KAExB,oBAAoB,UAAU,eACjC,yBAAyB;EACvB,SAAS;;KAGR,eAAe,UAAU,iBAAiB,4BACrC,aAAgB;iBAajB,eAAe,UAAU,aAAa,WAAW,IAwCtC,0BAChB,oBAAoB,KAAK,cAAc,aAAa;cAclD;;;;aAxEO;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA;;;;;aAAA"}
1
+ {"version":3,"file":"Primitive.d.ts","names":[],"sources":["../../src/utils/Primitive.tsx"],"mappings":";;;;;;;;;;;;cA0BM;KAmBD,wBAAwB;KAExB,oBAAoB,UAAU,eACjC,yBAAyB;EACvB,SAAS;;KAGR,eAAe,UAAU,iBAAiB,4BACrC,aAAgB;iBAajB,eAAe,UAAU,aAAa,WAAW,IAwCtC,0BAChB,oBAAoB,KAAK,cAAc,aAAa;cAclD;;;;IAxEO,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA;;;;;IAAA,SAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Primitive.js","names":["ComponentPropsWithoutRef","ComponentRef","ElementType","ForwardRefExoticComponent","PropsWithoutRef","ReactElement","ReactNode","RefAttributes","cloneElement","forwardRef","isValidElement","Primitive","RadixPrimitive","NODES","const","PrimitiveNode","WithRenderPropProps","T","render","PrimitiveProps","E","WithRenderPropRuntimeProps","asChild","children","PrimitiveRef","withRenderProp","Component","Wrapped","t0","ref","$","_c","rest","Comp","renderChildren","undefined","props","Record","t1","t2","t3","componentName","displayName","name","createPrimitive","node","RadixComp","reduce","acc","ReturnType","K"],"sources":["../../src/utils/Primitive.tsx"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n type ElementType,\n type ForwardRefExoticComponent,\n type PropsWithoutRef,\n type ReactElement,\n type ReactNode,\n type RefAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n} from \"react\";\nimport { Primitive as RadixPrimitive } from \"@radix-ui/react-primitive\";\n\n/**\n * Thin wrapper around `@radix-ui/react-primitive` that adds `render` prop support.\n *\n * When `render` is provided, it is converted to the equivalent `asChild` pattern:\n * render={<Comp props />} + children → asChild + <Comp props>{children}</Comp>\n *\n * All prop merging, ref composition, and event handler chaining remain handled\n * by Radix's battle-tested Slot implementation — we add zero custom logic for that.\n */\n\n// Match @radix-ui/react-primitive's full element set\nconst NODES = [\n \"a\",\n \"button\",\n \"div\",\n \"form\",\n \"h2\",\n \"h3\",\n \"img\",\n \"input\",\n \"label\",\n \"li\",\n \"nav\",\n \"ol\",\n \"p\",\n \"select\",\n \"span\",\n \"svg\",\n \"ul\",\n] as const;\ntype PrimitiveNode = (typeof NODES)[number];\n\ntype WithRenderPropProps<T extends ElementType> =\n ComponentPropsWithoutRef<T> & {\n render?: ReactElement | undefined;\n };\n\ntype PrimitiveProps<E extends PrimitiveNode> = WithRenderPropProps<\n (typeof RadixPrimitive)[E]\n>;\n\ntype WithRenderPropRuntimeProps<T extends ElementType> =\n WithRenderPropProps<T> & {\n asChild?: boolean | undefined;\n children?: ReactNode | undefined;\n };\n\ntype PrimitiveRef<E extends PrimitiveNode> = ComponentRef<\n (typeof RadixPrimitive)[E]\n>;\n\nfunction withRenderProp<T extends ElementType>(Component: T) {\n const Wrapped = forwardRef<ComponentRef<T>, WithRenderPropRuntimeProps<T>>(\n (\n {\n render,\n asChild,\n children,\n ...rest\n }: PropsWithoutRef<WithRenderPropRuntimeProps<T>>,\n ref,\n ) => {\n const Comp = Component as any;\n\n if (render && isValidElement(render)) {\n const renderChildren =\n children !== undefined\n ? children\n : ((render.props as Record<string, unknown>).children as ReactNode);\n\n return (\n <Comp {...(rest as any)} asChild ref={ref}>\n {cloneElement(render, undefined, renderChildren)}\n </Comp>\n );\n }\n\n return (\n <Comp {...(rest as any)} asChild={asChild} ref={ref}>\n {children}\n </Comp>\n );\n },\n );\n\n const componentName =\n typeof Component === \"string\"\n ? Component\n : (Component.displayName ?? Component.name ?? \"Component\");\n Wrapped.displayName = componentName;\n\n return Wrapped as ForwardRefExoticComponent<\n WithRenderPropProps<T> & RefAttributes<ComponentRef<T>>\n >;\n}\n\nfunction createPrimitive<E extends PrimitiveNode>(node: E) {\n const RadixComp = RadixPrimitive[node];\n const Component = withRenderProp(RadixComp);\n\n Component.displayName = `Primitive.${node}`;\n return Component as ForwardRefExoticComponent<\n PrimitiveProps<E> & RefAttributes<PrimitiveRef<E>>\n >;\n}\n\nconst Primitive = NODES.reduce(\n (acc, node) => {\n acc[node] = createPrimitive(node);\n return acc;\n },\n {} as {\n [K in PrimitiveNode]: ReturnType<typeof createPrimitive<K>>;\n },\n);\n\nexport { Primitive, withRenderProp };\nexport type { PrimitiveProps, WithRenderPropProps };\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAMa,QAAQ;CACZ;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAAI;AAuBN,SAASY,eAAsCC,WAAc;CAC3D,MAAMC,UAAUlB,YACdmB,IAAAC,QAAA;EAAA,MAAAC,IAAAC,EAAA,EAAA;EAAA,IAAAT;EAAA,IAAAC;EAAA,IAAAL;EAAA,IAAAc;EAAA,IAAAF,EAAA,OAAAF,IAAA;GACE,CAAA,CAAAV,QAAAI,SAAAC,aAAAS,QAAAJ;GAKiDE,EAAA,KAAAF;GAAAE,EAAA,KAAAR;GAAAQ,EAAA,KAAAP;GAAAO,EAAA,KAAAZ;GAAAY,EAAA,KAAAE;EAAA,OAAA;GAAAV,UAAAQ,EAAA;GAAAP,WAAAO,EAAA;GAAAZ,SAAAY,EAAA;GAAAE,OAAAF,EAAA;EAAA;EAGjD,MAAAG,OAAaP;EAEb,IAAIR,UAAUR,eAAeQ,MAAM,GAAC;GAClC,MAAAgB,iBACEX,aAAaY,KAAAA,IAAbZ,WAEML,OAAMkB,MAAiCb;GAGlC,MAAAe,KAAAN;GAAW,IAAAO;GAAA,IAAAT,EAAA,OAAAZ,UAAAY,EAAA,OAAAI,gBAAA;IACnBK,KAAA/B,aAAaU,QAAQiB,KAAAA,GAAWD,cAAc;IAACJ,EAAA,KAAAZ;IAAAY,EAAA,KAAAI;IAAAJ,EAAA,KAAAS;GAAA,OAAAA,KAAAT,EAAA;GAAA,IAAAU;GAAA,IAAAV,EAAA,OAAAD,OAAAC,EAAA,OAAAQ,MAAAR,EAAA,QAAAS,IAAA;IADlDC,KAAA,oBAAC,MAAD;KAAK,GAAMF;KAAc,SAAA;KAAaT;eACnCU;IADE,CAAA;IAEET,EAAA,KAAAD;IAAAC,EAAA,KAAAQ;IAAAR,EAAA,MAAAS;IAAAT,EAAA,MAAAU;GAAA,OAAAA,KAAAV,EAAA;GAAA,OAFPU;EAEO;EAKE,MAAAF,KAAAN;EAAW,IAAAO;EAAA,IAAAT,EAAA,QAAAR,WAAAQ,EAAA,QAAAP,YAAAO,EAAA,QAAAD,OAAAC,EAAA,QAAAQ,IAAA;GAAtBC,KAAA,oBAAC,MAAD;IAAK,GAAMD;IAAuBhB;IAAcO;IAC7CN;GADE,CAAA;GAEEO,EAAA,MAAAR;GAAAQ,EAAA,MAAAP;GAAAO,EAAA,MAAAD;GAAAC,EAAA,MAAAQ;GAAAR,EAAA,MAAAS;EAAA,OAAAA,KAAAT,EAAA;EAAA,OAFPS;CAEO,CAGb;CAMAZ,QAAQe,cAHN,OAAOhB,cAAc,WACjBA,YACCA,UAAUgB,eAAehB,UAAUiB,QAAQ;CAGlD,OAAOhB;AAGT;AAEA,SAASiB,gBAAyCC,MAAS;CACzD,MAAMC,YAAYlC,YAAeiC;CACjC,MAAMnB,YAAYD,eAAeqB,SAAS;CAE1CpB,UAAUgB,cAAc,aAAaG;CACrC,OAAOnB;AAGT;AAEA,MAAMf,YAAYE,MAAMkC,QACrBC,KAAKH,SAAS;CACbG,IAAIH,QAAQD,gBAAgBC,IAAI;CAChC,OAAOG;AACT,GACA,CAAC,CAGH"}
1
+ {"version":3,"file":"Primitive.js","names":["ComponentPropsWithoutRef","ComponentRef","ElementType","ForwardRefExoticComponent","PropsWithoutRef","ReactElement","ReactNode","RefAttributes","cloneElement","forwardRef","isValidElement","Primitive","RadixPrimitive","NODES","const","PrimitiveNode","WithRenderPropProps","T","render","PrimitiveProps","E","WithRenderPropRuntimeProps","asChild","children","PrimitiveRef","withRenderProp","Component","Wrapped","t0","ref","$","_c","rest","Comp","renderChildren","undefined","props","Record","t1","t2","t3","componentName","displayName","name","createPrimitive","node","RadixComp","reduce","acc","ReturnType","K"],"sources":["../../src/utils/Primitive.tsx"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n type ElementType,\n type ForwardRefExoticComponent,\n type PropsWithoutRef,\n type ReactElement,\n type ReactNode,\n type RefAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n} from \"react\";\nimport { Primitive as RadixPrimitive } from \"@radix-ui/react-primitive\";\n\n/**\n * Thin wrapper around `@radix-ui/react-primitive` that adds `render` prop support.\n *\n * When `render` is provided, it is converted to the equivalent `asChild` pattern:\n * render={<Comp props />} + children → asChild + <Comp props>{children}</Comp>\n *\n * All prop merging, ref composition, and event handler chaining remain handled\n * by Radix's battle-tested Slot implementation — we add zero custom logic for that.\n */\n\n// Match @radix-ui/react-primitive's full element set\nconst NODES = [\n \"a\",\n \"button\",\n \"div\",\n \"form\",\n \"h2\",\n \"h3\",\n \"img\",\n \"input\",\n \"label\",\n \"li\",\n \"nav\",\n \"ol\",\n \"p\",\n \"select\",\n \"span\",\n \"svg\",\n \"ul\",\n] as const;\ntype PrimitiveNode = (typeof NODES)[number];\n\ntype WithRenderPropProps<T extends ElementType> =\n ComponentPropsWithoutRef<T> & {\n render?: ReactElement | undefined;\n };\n\ntype PrimitiveProps<E extends PrimitiveNode> = WithRenderPropProps<\n (typeof RadixPrimitive)[E]\n>;\n\ntype WithRenderPropRuntimeProps<T extends ElementType> =\n WithRenderPropProps<T> & {\n asChild?: boolean | undefined;\n children?: ReactNode | undefined;\n };\n\ntype PrimitiveRef<E extends PrimitiveNode> = ComponentRef<\n (typeof RadixPrimitive)[E]\n>;\n\nfunction withRenderProp<T extends ElementType>(Component: T) {\n const Wrapped = forwardRef<ComponentRef<T>, WithRenderPropRuntimeProps<T>>(\n (\n {\n render,\n asChild,\n children,\n ...rest\n }: PropsWithoutRef<WithRenderPropRuntimeProps<T>>,\n ref,\n ) => {\n const Comp = Component as any;\n\n if (render && isValidElement(render)) {\n const renderChildren =\n children !== undefined\n ? children\n : ((render.props as Record<string, unknown>).children as ReactNode);\n\n return (\n <Comp {...(rest as any)} asChild ref={ref}>\n {cloneElement(render, undefined, renderChildren)}\n </Comp>\n );\n }\n\n return (\n <Comp {...(rest as any)} asChild={asChild} ref={ref}>\n {children}\n </Comp>\n );\n },\n );\n\n const componentName =\n typeof Component === \"string\"\n ? Component\n : (Component.displayName ?? Component.name ?? \"Component\");\n Wrapped.displayName = componentName;\n\n return Wrapped as ForwardRefExoticComponent<\n WithRenderPropProps<T> & RefAttributes<ComponentRef<T>>\n >;\n}\n\nfunction createPrimitive<E extends PrimitiveNode>(node: E) {\n const RadixComp = RadixPrimitive[node];\n const Component = withRenderProp(RadixComp);\n\n Component.displayName = `Primitive.${node}`;\n return Component as ForwardRefExoticComponent<\n PrimitiveProps<E> & RefAttributes<PrimitiveRef<E>>\n >;\n}\n\nconst Primitive = NODES.reduce(\n (acc, node) => {\n acc[node] = createPrimitive(node);\n return acc;\n },\n {} as {\n [K in PrimitiveNode]: ReturnType<typeof createPrimitive<K>>;\n },\n);\n\nexport { Primitive, withRenderProp };\nexport type { PrimitiveProps, WithRenderPropProps };\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAMa,QAAQ;CACZ;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAAI;AAuBN,SAASY,eAAsCC,WAAc;CAC3D,MAAMC,UAAUlB,YACdmB,IAAAC,QAAA;EAAA,MAAAC,IAAAC,EAAA,EAAA;EAAA,IAAAT;EAAA,IAAAC;EAAA,IAAAL;EAAA,IAAAc;EAAA,IAAAF,EAAA,OAAAF,IAAA;GACE,CAAA,CAAAV,QAAAI,SAAAC,aAAAS,QAAAJ;GAKiDE,EAAA,KAAAF;GAAAE,EAAA,KAAAR;GAAAQ,EAAA,KAAAP;GAAAO,EAAA,KAAAZ;GAAAY,EAAA,KAAAE;EAAA,OAAA;GAAAV,UAAAQ,EAAA;GAAAP,WAAAO,EAAA;GAAAZ,SAAAY,EAAA;GAAAE,OAAAF,EAAA;EAAA;EAGjD,MAAAG,OAAaP;EAEb,IAAIR,UAAUR,eAAeQ,MAAM,GAAC;GAClC,MAAAgB,iBACEX,aAAaY,KAAAA,IAAbZ,WAEML,OAAMkB,MAAiCb;GAGlC,MAAAe,KAAAN;GAAW,IAAAO;GAAA,IAAAT,EAAA,OAAAZ,UAAAY,EAAA,OAAAI,gBAAA;IACnBK,KAAA/B,aAAaU,QAAQiB,KAAAA,GAAWD,cAAc;IAACJ,EAAA,KAAAZ;IAAAY,EAAA,KAAAI;IAAAJ,EAAA,KAAAS;GAAA,OAAAA,KAAAT,EAAA;GAAA,IAAAU;GAAA,IAAAV,EAAA,OAAAD,OAAAC,EAAA,OAAAQ,MAAAR,EAAA,QAAAS,IAAA;IADlDC,KAAA,oBAAC,MAAD;KAAK,GAAMF;KAAc,SAAA;KAAaT;KACnCU,UAAAA;IADE,CAAA;IAEET,EAAA,KAAAD;IAAAC,EAAA,KAAAQ;IAAAR,EAAA,MAAAS;IAAAT,EAAA,MAAAU;GAAA,OAAAA,KAAAV,EAAA;GAAA,OAFPU;EAEO;EAKE,MAAAF,KAAAN;EAAW,IAAAO;EAAA,IAAAT,EAAA,QAAAR,WAAAQ,EAAA,QAAAP,YAAAO,EAAA,QAAAD,OAAAC,EAAA,QAAAQ,IAAA;GAAtBC,KAAA,oBAAC,MAAD;IAAK,GAAMD;IAAuBhB;IAAcO;IAC7CN;GADE,CAAA;GAEEO,EAAA,MAAAR;GAAAQ,EAAA,MAAAP;GAAAO,EAAA,MAAAD;GAAAC,EAAA,MAAAQ;GAAAR,EAAA,MAAAS;EAAA,OAAAA,KAAAT,EAAA;EAAA,OAFPS;CAEO,CAGb;CAMAZ,QAAQe,cAHN,OAAOhB,cAAc,WACjBA,YACCA,UAAUgB,eAAehB,UAAUiB,QAAQ;CAGlD,OAAOhB;AAGT;AAEA,SAASiB,gBAAyCC,MAAS;CACzD,MAAMC,YAAYlC,YAAeiC;CACjC,MAAMnB,YAAYD,eAAeqB,SAAS;CAE1CpB,UAAUgB,cAAc,aAAaG;CACrC,OAAOnB;AAGT;AAEA,MAAMf,YAAYE,MAAMkC,QACrBC,KAAKH,SAAS;CACbG,IAAIH,QAAQD,gBAAgBC,IAAI;CAChC,OAAOG;AACT,GACA,CAAC,CAGH"}
@@ -1 +1 @@
1
- {"version":3,"file":"createActionButton.d.ts","names":[],"sources":["../../src/utils/createActionButton.tsx"],"mappings":";;;KASK,qBAAqB,WACxB,OAAO,WACJ,kBAAkB;KAElB,uBAAuB,gCAAgC,UAAU;KAE1D,kBAAkB,SAAS,wBACpC,gBAAe,aAAa,sBAAqB;KAExC,sBAAsB,oBAAoB,UAAU;cAEnD,qBAAsB,QACjC,qBACA,iBAAiB,qBAAqB,SACtC,sBAAqB,YAAY,+BAAe,0CAAA,gBAAA,KAAA,qBAAA,gBAAA,qCAAA,qBAAA"}
1
+ {"version":3,"file":"createActionButton.d.ts","names":[],"sources":["../../src/utils/createActionButton.tsx"],"mappings":";;;KASK,qBAAqB,WACxB,OAAO,WACJ,kBAAkB;KAElB,uBAAuB,gCAAgC,UAAU;KAE1D,kBAAkB,SAAS,wBACpC,gBAAe,aAAa,sBAAqB;KAExC,sBAAsB,oBAAoB,UAAU;cAEnD,qBAAsB,QACjC,qBACA,iBAAiB,qBAAqB,SACtC,sBAAqB,YAAY,+BAAe,0CAAA,gBAAA,KAAA,qBAAA,gBAAA,qCAAA,qBAAA;;;;oBAgCjD,cAAA,6BAAA,0BAAA,cAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.14.27",
3
+ "version": "0.14.28",
4
4
  "description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
5
5
  "keywords": [
6
6
  "ai",
@@ -55,23 +55,23 @@
55
55
  ],
56
56
  "sideEffects": false,
57
57
  "dependencies": {
58
- "@assistant-ui/core": "^0.2.21",
59
- "@assistant-ui/store": "^0.2.20",
60
- "@assistant-ui/tap": "^0.9.4",
61
- "@radix-ui/primitive": "^1.1.5",
62
- "@radix-ui/react-collection": "^1.1.12",
63
- "@radix-ui/react-compose-refs": "^1.1.3",
64
- "@radix-ui/react-context": "^1.2.0",
65
- "@radix-ui/react-primitive": "^2.1.7",
66
- "@radix-ui/react-use-callback-ref": "^1.1.2",
67
- "@radix-ui/react-use-controllable-state": "^1.2.3",
68
- "@radix-ui/react-use-escape-keydown": "^1.1.3",
69
- "assistant-cloud": "^0.1.35",
70
- "assistant-stream": "^0.3.26",
58
+ "@assistant-ui/core": "^0.2.22",
59
+ "@assistant-ui/store": "^0.2.21",
60
+ "@assistant-ui/tap": "^0.9.5",
61
+ "@radix-ui/primitive": "^1.1.7",
62
+ "@radix-ui/react-collection": "^1.1.15",
63
+ "@radix-ui/react-compose-refs": "^1.1.5",
64
+ "@radix-ui/react-context": "^1.2.2",
65
+ "@radix-ui/react-primitive": "^2.1.10",
66
+ "@radix-ui/react-use-callback-ref": "^1.1.4",
67
+ "@radix-ui/react-use-controllable-state": "^1.2.6",
68
+ "@radix-ui/react-use-escape-keydown": "^1.1.5",
69
+ "assistant-cloud": "^0.1.36",
70
+ "assistant-stream": "^0.3.27",
71
71
  "nanoid": "^6.0.0",
72
- "radix-ui": "^1.6.2",
72
+ "radix-ui": "^1.6.7",
73
73
  "react-textarea-autosize": "^8.5.9",
74
- "safe-content-frame": "^0.0.23",
74
+ "safe-content-frame": "^0.0.24",
75
75
  "zod": "^4.4.3",
76
76
  "zustand": "^5.0.14"
77
77
  },
@@ -96,11 +96,11 @@
96
96
  "@types/react": "^19.2.17",
97
97
  "@types/react-dom": "^19.2.3",
98
98
  "jsdom": "^29.1.1",
99
- "react": "^19.2.7",
100
- "react-dom": "^19.2.7",
99
+ "react": "^19.2.8",
100
+ "react-dom": "^19.2.8",
101
101
  "vitest": "^4.1.10",
102
- "@assistant-ui/vite": "0.0.9",
103
- "@assistant-ui/x-buildutils": "0.0.18"
102
+ "@assistant-ui/vite": "0.0.10",
103
+ "@assistant-ui/x-buildutils": "0.0.19"
104
104
  },
105
105
  "publishConfig": {
106
106
  "access": "public",
@@ -24,6 +24,7 @@ import type { QueueItemState } from "@assistant-ui/core/store";
24
24
  import type { ComposerSendOptions } from "@assistant-ui/core/store";
25
25
  import {
26
26
  getThreadMessageText,
27
+ isCreateAttachment,
27
28
  resolveToolApprovalResponse,
28
29
  } from "@assistant-ui/core/internal";
29
30
  import { ModelContext, Suggestions } from "@assistant-ui/core/store";
@@ -422,7 +423,7 @@ const useComposerClientResource = ({
422
423
  setRole,
423
424
  setRunConfig,
424
425
  addAttachment: async (fileOrAttachment: File | CreateAttachment) => {
425
- if (fileOrAttachment instanceof File) {
426
+ if (!isCreateAttachment(fileOrAttachment)) {
426
427
  const newAttachment: Attachment = {
427
428
  id: Math.random().toString(36).substring(7),
428
429
  type: "file",
@@ -0,0 +1,146 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act, cleanup, render } from "@testing-library/react";
4
+ import {
5
+ forwardRef,
6
+ useState,
7
+ type ComponentProps,
8
+ type ChangeEvent,
9
+ } from "react";
10
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
+
12
+ const { registerMock, auiMock } = vi.hoisted(() => {
13
+ const register = vi.fn((_provider: unknown) => () => {});
14
+ return {
15
+ registerMock: register,
16
+ auiMock: {
17
+ modelContext: () => ({ register }),
18
+ },
19
+ };
20
+ });
21
+
22
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
23
+ ...(await importOriginal()),
24
+ useAui: () => auiMock,
25
+ }));
26
+
27
+ import { makeAssistantVisible } from "./makeAssistantVisible";
28
+
29
+ type EditTool = {
30
+ execute: (args: { editId: string; value: string }) => Promise<unknown>;
31
+ };
32
+
33
+ type ModelContextProvider = {
34
+ getModelContext: () => {
35
+ tools: {
36
+ edit: EditTool;
37
+ };
38
+ };
39
+ };
40
+
41
+ const ControlledInput = forwardRef<HTMLInputElement, ComponentProps<"input">>(
42
+ ({ onChange, ...props }, ref) => {
43
+ const [value, setValue] = useState("initial");
44
+ const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
45
+ setValue(event.target.value);
46
+ onChange?.(event);
47
+ };
48
+
49
+ return (
50
+ <input
51
+ {...props}
52
+ ref={ref}
53
+ value={value}
54
+ data-react-value={value}
55
+ onChange={handleChange}
56
+ />
57
+ );
58
+ },
59
+ );
60
+
61
+ const ControlledTextarea = forwardRef<
62
+ HTMLTextAreaElement,
63
+ ComponentProps<"textarea">
64
+ >(({ onChange, ...props }, ref) => {
65
+ const [value, setValue] = useState("initial");
66
+ const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
67
+ setValue(event.target.value);
68
+ onChange?.(event);
69
+ };
70
+
71
+ return (
72
+ <textarea
73
+ {...props}
74
+ ref={ref}
75
+ value={value}
76
+ data-react-value={value}
77
+ onChange={handleChange}
78
+ />
79
+ );
80
+ });
81
+
82
+ const runEditTool = async (
83
+ element: HTMLInputElement | HTMLTextAreaElement,
84
+ value: string,
85
+ ) => {
86
+ const editId = element.dataset.editId;
87
+ if (editId === undefined) throw new Error("Missing edit id");
88
+
89
+ const provider = registerMock.mock.calls[0]![0] as ModelContextProvider;
90
+ const task = provider.getModelContext().tools.edit.execute({
91
+ editId,
92
+ value,
93
+ });
94
+ await vi.runAllTimersAsync();
95
+ await task;
96
+ };
97
+
98
+ describe("makeAssistantVisible editable components", () => {
99
+ beforeEach(() => {
100
+ registerMock.mockClear();
101
+ vi.stubGlobal("CSS", { escape: (value: string) => value });
102
+ vi.useFakeTimers();
103
+ });
104
+
105
+ afterEach(() => {
106
+ cleanup();
107
+ vi.unstubAllGlobals();
108
+ vi.useRealTimers();
109
+ });
110
+
111
+ it("updates controlled input state through the edit tool", async () => {
112
+ const EditableInput = makeAssistantVisible(ControlledInput, {
113
+ editable: true,
114
+ });
115
+ const onChange = vi.fn();
116
+ const input = render(<EditableInput onChange={onChange} />).getByRole(
117
+ "textbox",
118
+ );
119
+
120
+ await act(async () => {
121
+ await runEditTool(input as HTMLInputElement, "updated");
122
+ });
123
+
124
+ expect(input).toHaveProperty("value", "updated");
125
+ expect(input.getAttribute("data-react-value")).toBe("updated");
126
+ expect(onChange).toHaveBeenCalledOnce();
127
+ });
128
+
129
+ it("updates controlled textarea state through the edit tool", async () => {
130
+ const EditableTextarea = makeAssistantVisible(ControlledTextarea, {
131
+ editable: true,
132
+ });
133
+ const onChange = vi.fn();
134
+ const textarea = render(<EditableTextarea onChange={onChange} />).getByRole(
135
+ "textbox",
136
+ );
137
+
138
+ await act(async () => {
139
+ await runEditTool(textarea as HTMLTextAreaElement, "updated");
140
+ });
141
+
142
+ expect(textarea).toHaveProperty("value", "updated");
143
+ expect(textarea.getAttribute("data-react-value")).toBe("updated");
144
+ expect(onChange).toHaveBeenCalledOnce();
145
+ });
146
+ });
@@ -40,6 +40,23 @@ const click = tool({
40
40
  },
41
41
  });
42
42
 
43
+ const setNativeValue = (
44
+ element: HTMLInputElement | HTMLTextAreaElement,
45
+ value: string,
46
+ ) => {
47
+ const prototype =
48
+ element instanceof HTMLInputElement
49
+ ? HTMLInputElement.prototype
50
+ : HTMLTextAreaElement.prototype;
51
+ const setter = Object.getOwnPropertyDescriptor(prototype, "value")?.set;
52
+
53
+ if (setter) {
54
+ setter.call(element, value);
55
+ } else {
56
+ element.value = value;
57
+ }
58
+ };
59
+
43
60
  const edit = tool({
44
61
  parameters: {
45
62
  type: "object",
@@ -57,7 +74,7 @@ const edit = tool({
57
74
  const escapedEditId = CSS.escape(editId);
58
75
  const el = document.querySelector(`[data-edit-id='${escapedEditId}']`);
59
76
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
60
- el.value = value;
77
+ setNativeValue(el, value);
61
78
  el.dispatchEvent(new Event("input", { bubbles: true }));
62
79
  el.dispatchEvent(new Event("change", { bubbles: true }));
63
80
 
@@ -1,12 +1,12 @@
1
- /**
2
- * @vitest-environment jsdom
3
- */
1
+ /** @vitest-environment jsdom */
4
2
  import { act } from "react";
5
- import type { ReactNode } from "react";
3
+ import type { MouseEvent, ReactNode } from "react";
4
+ import { createPortal } from "react-dom";
6
5
  import { createRoot } from "react-dom/client";
7
6
  import type { Root } from "react-dom/client";
8
7
  import type * as AssistantStore from "@assistant-ui/store";
9
8
  import type * as ComposerSendModule from "./ComposerSend";
9
+ import { fireEvent, render } from "@testing-library/react";
10
10
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
11
11
  import { useComposerCompactContextOptional } from "./ComposerCompactContext";
12
12
  import type { ComposerCompactContextValue } from "./ComposerCompactContext";
@@ -58,7 +58,228 @@ const resetStoreState = () => {
58
58
  storeState.composer.text = "";
59
59
  };
60
60
 
61
- describe("ComposerPrimitiveRoot", () => {
61
+ describe("ComposerPrimitiveRoot click-to-focus", () => {
62
+ beforeEach(() => {
63
+ resetStoreState();
64
+ });
65
+
66
+ const setupComposer = (props?: ComposerPrimitiveRoot.Props) => {
67
+ const { getByTestId } = render(
68
+ <ComposerPrimitiveRoot {...props}>
69
+ <div data-testid="blank-space">
70
+ <textarea data-testid="composer-input" />
71
+ </div>
72
+ </ComposerPrimitiveRoot>,
73
+ );
74
+ return {
75
+ blankSpace: getByTestId("blank-space"),
76
+ textarea: getByTestId("composer-input"),
77
+ };
78
+ };
79
+
80
+ it("focuses the textarea when primary-button mousedown starts on blank form space", () => {
81
+ const { blankSpace, textarea } = setupComposer();
82
+
83
+ fireEvent.mouseDown(blankSpace, { button: 0 });
84
+
85
+ expect(document.activeElement).toBe(textarea);
86
+ });
87
+
88
+ it("does not move focus to the textarea when mousedown starts on an interactive child", () => {
89
+ const { getByTestId } = render(
90
+ <ComposerPrimitiveRoot>
91
+ <button data-testid="interactive-child" type="button">
92
+ Action
93
+ </button>
94
+ <textarea data-testid="composer-input" />
95
+ </ComposerPrimitiveRoot>,
96
+ );
97
+
98
+ const notPrevented = fireEvent.mouseDown(getByTestId("interactive-child"), {
99
+ button: 0,
100
+ });
101
+
102
+ expect(notPrevented).toBe(true);
103
+ expect(document.activeElement).not.toBe(getByTestId("composer-input"));
104
+ });
105
+
106
+ it("does not steal focus from a role-based widget carrying a tabindex", () => {
107
+ const { getByTestId } = render(
108
+ <ComposerPrimitiveRoot>
109
+ <div role="slider" tabIndex={0} data-testid="widget" />
110
+ <textarea data-testid="composer-input" />
111
+ </ComposerPrimitiveRoot>,
112
+ );
113
+
114
+ const notPrevented = fireEvent.mouseDown(getByTestId("widget"), {
115
+ button: 0,
116
+ });
117
+
118
+ expect(notPrevented).toBe(true);
119
+ expect(document.activeElement).not.toBe(getByTestId("composer-input"));
120
+ });
121
+
122
+ it("does not steal focus from a roving-tabindex child with tabindex=-1", () => {
123
+ const { getByTestId } = render(
124
+ <ComposerPrimitiveRoot>
125
+ <span tabIndex={-1} data-testid="roving-item">
126
+ Item
127
+ </span>
128
+ <textarea data-testid="composer-input" />
129
+ </ComposerPrimitiveRoot>,
130
+ );
131
+
132
+ const notPrevented = fireEvent.mouseDown(getByTestId("roving-item"), {
133
+ button: 0,
134
+ });
135
+
136
+ expect(notPrevented).toBe(true);
137
+ expect(document.activeElement).not.toBe(getByTestId("composer-input"));
138
+ });
139
+
140
+ it("does not move focus to the textarea on non-primary mousedown", () => {
141
+ const { blankSpace, textarea } = setupComposer();
142
+
143
+ const notPrevented = fireEvent.mouseDown(blankSpace, { button: 2 });
144
+
145
+ expect(notPrevented).toBe(true);
146
+ expect(document.activeElement).not.toBe(textarea);
147
+ });
148
+
149
+ it("focuses a contenteditable composer target when primary-button mousedown starts on blank form space", () => {
150
+ const { getByTestId } = render(
151
+ <ComposerPrimitiveRoot>
152
+ <div data-testid="blank-space">
153
+ <div contentEditable data-testid="composer-input" tabIndex={0} />
154
+ </div>
155
+ </ComposerPrimitiveRoot>,
156
+ );
157
+
158
+ fireEvent.mouseDown(getByTestId("blank-space"), { button: 0 });
159
+
160
+ expect(document.activeElement).toBe(getByTestId("composer-input"));
161
+ });
162
+
163
+ it("still focuses the textarea when the form is hosted inside a tabindex=-1 wrapper", () => {
164
+ const { getByTestId } = render(
165
+ <div tabIndex={-1}>
166
+ <ComposerPrimitiveRoot>
167
+ <div data-testid="blank-space">
168
+ <textarea data-testid="composer-input" />
169
+ </div>
170
+ </ComposerPrimitiveRoot>
171
+ </div>,
172
+ );
173
+
174
+ fireEvent.mouseDown(getByTestId("blank-space"), { button: 0 });
175
+
176
+ expect(document.activeElement).toBe(getByTestId("composer-input"));
177
+ });
178
+
179
+ it("ignores mousedown on portaled descendants rendered outside the form's DOM", () => {
180
+ const { getByTestId } = render(
181
+ <ComposerPrimitiveRoot>
182
+ {createPortal(<div data-testid="portal-blank" />, document.body)}
183
+ <textarea data-testid="composer-input" />
184
+ </ComposerPrimitiveRoot>,
185
+ );
186
+
187
+ const notPrevented = fireEvent.mouseDown(getByTestId("portal-blank"), {
188
+ button: 0,
189
+ });
190
+
191
+ expect(notPrevented).toBe(true);
192
+ expect(document.activeElement).not.toBe(getByTestId("composer-input"));
193
+ });
194
+
195
+ it("keeps the mousedown default when the only textarea is disabled", () => {
196
+ const { getByTestId } = render(
197
+ <ComposerPrimitiveRoot>
198
+ <div data-testid="blank-space">
199
+ <textarea disabled data-testid="composer-input" />
200
+ </div>
201
+ </ComposerPrimitiveRoot>,
202
+ );
203
+
204
+ const notPrevented = fireEvent.mouseDown(getByTestId("blank-space"), {
205
+ button: 0,
206
+ });
207
+
208
+ expect(notPrevented).toBe(true);
209
+ expect(document.activeElement).not.toBe(getByTestId("composer-input"));
210
+ });
211
+
212
+ it("skips a disabled textarea and focuses the next composer input", () => {
213
+ const { getByTestId } = render(
214
+ <ComposerPrimitiveRoot>
215
+ <div data-testid="blank-space">
216
+ <textarea disabled data-testid="disabled-input" />
217
+ <div contentEditable data-testid="composer-input" tabIndex={0} />
218
+ </div>
219
+ </ComposerPrimitiveRoot>,
220
+ );
221
+
222
+ fireEvent.mouseDown(getByTestId("blank-space"), { button: 0 });
223
+
224
+ expect(document.activeElement).toBe(getByTestId("composer-input"));
225
+ });
226
+
227
+ it("keeps the mousedown default when the form contains no composer input", () => {
228
+ const { getByTestId } = render(
229
+ <ComposerPrimitiveRoot>
230
+ <div data-testid="blank-space" />
231
+ </ComposerPrimitiveRoot>,
232
+ );
233
+
234
+ const notPrevented = fireEvent.mouseDown(getByTestId("blank-space"), {
235
+ button: 0,
236
+ });
237
+
238
+ expect(notPrevented).toBe(true);
239
+ });
240
+
241
+ it("lets descendants keep native mousedown defaults via stopPropagation", () => {
242
+ const { getByTestId } = render(
243
+ <ComposerPrimitiveRoot>
244
+ <div data-testid="selectable" onMouseDown={(e) => e.stopPropagation()}>
245
+ Selectable content
246
+ </div>
247
+ <textarea data-testid="composer-input" />
248
+ </ComposerPrimitiveRoot>,
249
+ );
250
+
251
+ const notPrevented = fireEvent.mouseDown(getByTestId("selectable"), {
252
+ button: 0,
253
+ });
254
+
255
+ expect(notPrevented).toBe(true);
256
+ expect(document.activeElement).not.toBe(getByTestId("composer-input"));
257
+ });
258
+
259
+ it("lets consumer onMouseDown prevent the default focus behavior", () => {
260
+ const onMouseDown = vi.fn((event: MouseEvent<HTMLFormElement>) => {
261
+ event.preventDefault();
262
+ });
263
+ const { blankSpace, textarea } = setupComposer({ onMouseDown });
264
+
265
+ fireEvent.mouseDown(blankSpace, { button: 0 });
266
+
267
+ expect(onMouseDown).toHaveBeenCalledTimes(1);
268
+ expect(document.activeElement).not.toBe(textarea);
269
+ });
270
+
271
+ it("runs consumer onMouseDown and preserves default focus behavior when it does not prevent default", () => {
272
+ const onMouseDown = vi.fn();
273
+ const { blankSpace, textarea } = setupComposer({ onMouseDown });
274
+
275
+ fireEvent.mouseDown(blankSpace, { button: 0 });
276
+
277
+ expect(onMouseDown).toHaveBeenCalledTimes(1);
278
+ expect(document.activeElement).toBe(textarea);
279
+ });
280
+ });
281
+
282
+ describe("ComposerPrimitiveRoot compact mode", () => {
62
283
  let container: HTMLDivElement;
63
284
  let root: Root;
64
285