@ulearning/ai-sdk 0.0.0-stage → 1.0.0-next.0

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 (1133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +12 -2
  3. package/dist/_virtual/_rolldown/runtime.js +12 -0
  4. package/dist/adapters/OpenAIRealtimeVoiceAdapter.d.ts +22 -0
  5. package/dist/adapters/OpenAIRealtimeVoiceAdapter.d.ts.map +1 -0
  6. package/dist/adapters/OpenAIRealtimeVoiceAdapter.js +296 -0
  7. package/dist/adapters/OpenAIRealtimeVoiceAdapter.js.map +1 -0
  8. package/dist/assistant-transport.d.ts +33 -0
  9. package/dist/assistant-transport.d.ts.map +1 -0
  10. package/dist/assistant-transport.js +18 -0
  11. package/dist/assistant-transport.js.map +1 -0
  12. package/dist/augmentations.d.ts +33 -0
  13. package/dist/augmentations.d.ts.map +1 -0
  14. package/dist/augmentations.js +0 -0
  15. package/dist/context/ReadonlyStore.d.ts +4 -0
  16. package/dist/context/ReadonlyStore.d.ts.map +1 -0
  17. package/dist/context/ReadonlyStore.js +7 -0
  18. package/dist/context/ReadonlyStore.js.map +1 -0
  19. package/dist/context/index.d.ts +13 -0
  20. package/dist/context/index.d.ts.map +1 -0
  21. package/dist/context/index.js +12 -0
  22. package/dist/context/providers/AttachmentByIndexProvider.d.ts +2 -0
  23. package/dist/context/providers/AttachmentByIndexProvider.d.ts.map +1 -0
  24. package/dist/context/providers/AttachmentByIndexProvider.js +2 -0
  25. package/dist/context/providers/ChainOfThoughtByIndicesProvider.d.ts +2 -0
  26. package/dist/context/providers/ChainOfThoughtByIndicesProvider.d.ts.map +1 -0
  27. package/dist/context/providers/ChainOfThoughtByIndicesProvider.js +2 -0
  28. package/dist/context/providers/MessageByIndexProvider.d.ts +2 -0
  29. package/dist/context/providers/MessageByIndexProvider.d.ts.map +1 -0
  30. package/dist/context/providers/MessageByIndexProvider.js +2 -0
  31. package/dist/context/providers/MessageProvider.d.ts +3 -0
  32. package/dist/context/providers/MessageProvider.d.ts.map +1 -0
  33. package/dist/context/providers/MessageProvider.js +25 -0
  34. package/dist/context/providers/MessageProvider.js.map +1 -0
  35. package/dist/context/providers/PartByIndexProvider.d.ts +2 -0
  36. package/dist/context/providers/PartByIndexProvider.d.ts.map +1 -0
  37. package/dist/context/providers/PartByIndexProvider.js +2 -0
  38. package/dist/context/providers/SuggestionByIndexProvider.d.ts +2 -0
  39. package/dist/context/providers/SuggestionByIndexProvider.d.ts.map +1 -0
  40. package/dist/context/providers/SuggestionByIndexProvider.js +2 -0
  41. package/dist/context/providers/TextMessagePartProvider.d.ts +2 -0
  42. package/dist/context/providers/TextMessagePartProvider.d.ts.map +1 -0
  43. package/dist/context/providers/TextMessagePartProvider.js +2 -0
  44. package/dist/context/providers/ThreadListItemProvider.d.ts +2 -0
  45. package/dist/context/providers/ThreadListItemProvider.d.ts.map +1 -0
  46. package/dist/context/providers/ThreadListItemProvider.js +2 -0
  47. package/dist/context/providers/ThreadViewportProvider.d.ts +6 -0
  48. package/dist/context/providers/ThreadViewportProvider.d.ts.map +1 -0
  49. package/dist/context/providers/ThreadViewportProvider.js +32 -0
  50. package/dist/context/providers/ThreadViewportProvider.js.map +1 -0
  51. package/dist/context/stores/ThreadViewport.d.ts +88 -0
  52. package/dist/context/stores/ThreadViewport.d.ts.map +1 -0
  53. package/dist/context/stores/ThreadViewport.js +130 -0
  54. package/dist/context/stores/ThreadViewport.js.map +1 -0
  55. package/dist/context/vue/ThreadViewportContext.d.ts +24 -0
  56. package/dist/context/vue/ThreadViewportContext.d.ts.map +1 -0
  57. package/dist/context/vue/ThreadViewportContext.js +10 -0
  58. package/dist/context/vue/ThreadViewportContext.js.map +1 -0
  59. package/dist/context/vue/utils/createContextHook.d.ts +11 -0
  60. package/dist/context/vue/utils/createContextHook.d.ts.map +1 -0
  61. package/dist/context/vue/utils/createContextHook.js +20 -0
  62. package/dist/context/vue/utils/createContextHook.js.map +1 -0
  63. package/dist/context/vue/utils/createContextStoreHook.d.ts +22 -0
  64. package/dist/context/vue/utils/createContextStoreHook.d.ts.map +1 -0
  65. package/dist/context/vue/utils/createContextStoreHook.js +48 -0
  66. package/dist/context/vue/utils/createContextStoreHook.js.map +1 -0
  67. package/dist/devtools/DevToolsHooks.d.ts +32 -0
  68. package/dist/devtools/DevToolsHooks.d.ts.map +1 -0
  69. package/dist/devtools/DevToolsHooks.js +84 -0
  70. package/dist/devtools/DevToolsHooks.js.map +1 -0
  71. package/dist/hooks/useMessageQuote.d.ts +17 -0
  72. package/dist/hooks/useMessageQuote.d.ts.map +1 -0
  73. package/dist/hooks/useMessageQuote.js +23 -0
  74. package/dist/hooks/useMessageQuote.js.map +1 -0
  75. package/dist/hooks/useMessageTiming.d.ts +17 -0
  76. package/dist/hooks/useMessageTiming.d.ts.map +1 -0
  77. package/dist/hooks/useMessageTiming.js +22 -0
  78. package/dist/hooks/useMessageTiming.js.map +1 -0
  79. package/dist/hooks/useToolCallElapsed.d.ts +22 -0
  80. package/dist/hooks/useToolCallElapsed.d.ts.map +1 -0
  81. package/dist/hooks/useToolCallElapsed.js +54 -0
  82. package/dist/hooks/useToolCallElapsed.js.map +1 -0
  83. package/dist/index.d.ts +155 -0
  84. package/dist/index.d.ts.map +1 -0
  85. package/dist/index.js +64 -0
  86. package/dist/internal.d.ts +9 -0
  87. package/dist/internal.d.ts.map +1 -0
  88. package/dist/internal.js +24 -0
  89. package/dist/internal.js.map +1 -0
  90. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts +13 -0
  91. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -0
  92. package/dist/legacy-runtime/AssistantRuntimeProvider.js +37 -0
  93. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -0
  94. package/dist/mcp-apps/McpAppRenderer.d.ts +60 -0
  95. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -0
  96. package/dist/mcp-apps/McpAppRenderer.js +223 -0
  97. package/dist/mcp-apps/McpAppRenderer.js.map +1 -0
  98. package/dist/mcp-apps/McpAppsRemoteHost.d.ts +3 -0
  99. package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -0
  100. package/dist/mcp-apps/McpAppsRemoteHost.js +101 -0
  101. package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -0
  102. package/dist/mcp-apps/app-frame.d.ts +3 -0
  103. package/dist/mcp-apps/app-frame.d.ts.map +1 -0
  104. package/dist/mcp-apps/app-frame.js +213 -0
  105. package/dist/mcp-apps/app-frame.js.map +1 -0
  106. package/dist/mcp-apps/bridge.d.ts +18 -0
  107. package/dist/mcp-apps/bridge.d.ts.map +1 -0
  108. package/dist/mcp-apps/bridge.js +319 -0
  109. package/dist/mcp-apps/bridge.js.map +1 -0
  110. package/dist/mcp-apps/csp.d.ts +4 -0
  111. package/dist/mcp-apps/csp.d.ts.map +1 -0
  112. package/dist/mcp-apps/csp.js +92 -0
  113. package/dist/mcp-apps/csp.js.map +1 -0
  114. package/dist/mcp-apps/index.d.ts +5 -0
  115. package/dist/mcp-apps/index.d.ts.map +1 -0
  116. package/dist/mcp-apps/index.js +4 -0
  117. package/dist/mcp-apps/types.d.ts +161 -0
  118. package/dist/mcp-apps/types.d.ts.map +1 -0
  119. package/dist/mcp-apps/types.js +6 -0
  120. package/dist/mcp-apps/types.js.map +1 -0
  121. package/dist/mcp-apps/utils.d.ts +12 -0
  122. package/dist/mcp-apps/utils.d.ts.map +1 -0
  123. package/dist/mcp-apps/utils.js +18 -0
  124. package/dist/mcp-apps/utils.js.map +1 -0
  125. package/dist/model-context/frame/useAssistantFrameHost.d.ts +35 -0
  126. package/dist/model-context/frame/useAssistantFrameHost.d.ts.map +1 -0
  127. package/dist/model-context/frame/useAssistantFrameHost.js +109 -0
  128. package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -0
  129. package/dist/model-context/makeAssistantVisible.d.ts +11 -0
  130. package/dist/model-context/makeAssistantVisible.d.ts.map +1 -0
  131. package/dist/model-context/makeAssistantVisible.js +102 -0
  132. package/dist/model-context/makeAssistantVisible.js.map +1 -0
  133. package/dist/primitives/actionBar/ActionBarCopy.d.ts +45 -0
  134. package/dist/primitives/actionBar/ActionBarCopy.d.ts.map +1 -0
  135. package/dist/primitives/actionBar/ActionBarCopy.js +71 -0
  136. package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -0
  137. package/dist/primitives/actionBar/ActionBarEdit.d.ts +55 -0
  138. package/dist/primitives/actionBar/ActionBarEdit.d.ts.map +1 -0
  139. package/dist/primitives/actionBar/ActionBarEdit.js +47 -0
  140. package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -0
  141. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts +26 -0
  142. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts.map +1 -0
  143. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +39 -0
  144. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -0
  145. package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts +13 -0
  146. package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts.map +1 -0
  147. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +28 -0
  148. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -0
  149. package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts +13 -0
  150. package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts.map +1 -0
  151. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +28 -0
  152. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -0
  153. package/dist/primitives/actionBar/ActionBarInteractionContext.d.ts +7 -0
  154. package/dist/primitives/actionBar/ActionBarInteractionContext.d.ts.map +1 -0
  155. package/dist/primitives/actionBar/ActionBarInteractionContext.js +8 -0
  156. package/dist/primitives/actionBar/ActionBarInteractionContext.js.map +1 -0
  157. package/dist/primitives/actionBar/ActionBarReload.d.ts +56 -0
  158. package/dist/primitives/actionBar/ActionBarReload.d.ts.map +1 -0
  159. package/dist/primitives/actionBar/ActionBarReload.js +48 -0
  160. package/dist/primitives/actionBar/ActionBarReload.js.map +1 -0
  161. package/dist/primitives/actionBar/ActionBarRoot.d.ts +78 -0
  162. package/dist/primitives/actionBar/ActionBarRoot.d.ts.map +1 -0
  163. package/dist/primitives/actionBar/ActionBarRoot.js +69 -0
  164. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -0
  165. package/dist/primitives/actionBar/ActionBarSpeak.d.ts +17 -0
  166. package/dist/primitives/actionBar/ActionBarSpeak.d.ts.map +1 -0
  167. package/dist/primitives/actionBar/ActionBarSpeak.js +13 -0
  168. package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -0
  169. package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts +17 -0
  170. package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts.map +1 -0
  171. package/dist/primitives/actionBar/ActionBarStopSpeaking.js +13 -0
  172. package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -0
  173. package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts +15 -0
  174. package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts.map +1 -0
  175. package/dist/primitives/actionBar/useActionBarFloatStatus.js +24 -0
  176. package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -0
  177. package/dist/primitives/actionBar.d.ts +10 -0
  178. package/dist/primitives/actionBar.d.ts.map +1 -0
  179. package/dist/primitives/actionBar.js +25 -0
  180. package/dist/primitives/actionBar.js.map +1 -0
  181. package/dist/primitives/actionBarMore/ActionBarMoreContent.d.ts +47 -0
  182. package/dist/primitives/actionBarMore/ActionBarMoreContent.d.ts.map +1 -0
  183. package/dist/primitives/actionBarMore/ActionBarMoreContent.js +29 -0
  184. package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -0
  185. package/dist/primitives/actionBarMore/ActionBarMoreItem.d.ts +19 -0
  186. package/dist/primitives/actionBarMore/ActionBarMoreItem.d.ts.map +1 -0
  187. package/dist/primitives/actionBarMore/ActionBarMoreItem.js +24 -0
  188. package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -0
  189. package/dist/primitives/actionBarMore/ActionBarMoreRoot.d.ts +10 -0
  190. package/dist/primitives/actionBarMore/ActionBarMoreRoot.d.ts.map +1 -0
  191. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js +39 -0
  192. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -0
  193. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.d.ts +16 -0
  194. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.d.ts.map +1 -0
  195. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js +24 -0
  196. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -0
  197. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.d.ts +17 -0
  198. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.d.ts.map +1 -0
  199. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js +24 -0
  200. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -0
  201. package/dist/primitives/actionBarMore/scope.d.ts +5 -0
  202. package/dist/primitives/actionBarMore/scope.d.ts.map +1 -0
  203. package/dist/primitives/actionBarMore/scope.js +7 -0
  204. package/dist/primitives/actionBarMore/scope.js.map +1 -0
  205. package/dist/primitives/actionBarMore.d.ts +6 -0
  206. package/dist/primitives/actionBarMore.d.ts.map +1 -0
  207. package/dist/primitives/actionBarMore.js +17 -0
  208. package/dist/primitives/actionBarMore.js.map +1 -0
  209. package/dist/primitives/assistantModal/AssistantModalAnchor.d.ts +17 -0
  210. package/dist/primitives/assistantModal/AssistantModalAnchor.d.ts.map +1 -0
  211. package/dist/primitives/assistantModal/AssistantModalAnchor.js +24 -0
  212. package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -0
  213. package/dist/primitives/assistantModal/AssistantModalContent.d.ts +50 -0
  214. package/dist/primitives/assistantModal/AssistantModalContent.d.ts.map +1 -0
  215. package/dist/primitives/assistantModal/AssistantModalContent.js +42 -0
  216. package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -0
  217. package/dist/primitives/assistantModal/AssistantModalRoot.d.ts +14 -0
  218. package/dist/primitives/assistantModal/AssistantModalRoot.d.ts.map +1 -0
  219. package/dist/primitives/assistantModal/AssistantModalRoot.js +35 -0
  220. package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -0
  221. package/dist/primitives/assistantModal/AssistantModalTrigger.d.ts +16 -0
  222. package/dist/primitives/assistantModal/AssistantModalTrigger.d.ts.map +1 -0
  223. package/dist/primitives/assistantModal/AssistantModalTrigger.js +24 -0
  224. package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -0
  225. package/dist/primitives/assistantModal/popoverRenderPrimitives.d.ts +139 -0
  226. package/dist/primitives/assistantModal/popoverRenderPrimitives.d.ts.map +1 -0
  227. package/dist/primitives/assistantModal/popoverRenderPrimitives.js +9 -0
  228. package/dist/primitives/assistantModal/popoverRenderPrimitives.js.map +1 -0
  229. package/dist/primitives/assistantModal/scope.d.ts +5 -0
  230. package/dist/primitives/assistantModal/scope.d.ts.map +1 -0
  231. package/dist/primitives/assistantModal/scope.js +7 -0
  232. package/dist/primitives/assistantModal/scope.js.map +1 -0
  233. package/dist/primitives/assistantModal.d.ts +5 -0
  234. package/dist/primitives/assistantModal.d.ts.map +1 -0
  235. package/dist/primitives/assistantModal.js +15 -0
  236. package/dist/primitives/assistantModal.js.map +1 -0
  237. package/dist/primitives/attachment/AttachmentName.d.ts +5 -0
  238. package/dist/primitives/attachment/AttachmentName.d.ts.map +1 -0
  239. package/dist/primitives/attachment/AttachmentName.js +14 -0
  240. package/dist/primitives/attachment/AttachmentName.js.map +1 -0
  241. package/dist/primitives/attachment/AttachmentRemove.d.ts +17 -0
  242. package/dist/primitives/attachment/AttachmentRemove.d.ts.map +1 -0
  243. package/dist/primitives/attachment/AttachmentRemove.js +15 -0
  244. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -0
  245. package/dist/primitives/attachment/AttachmentRoot.d.ts +30 -0
  246. package/dist/primitives/attachment/AttachmentRoot.d.ts.map +1 -0
  247. package/dist/primitives/attachment/AttachmentRoot.js +40 -0
  248. package/dist/primitives/attachment/AttachmentRoot.js.map +1 -0
  249. package/dist/primitives/attachment/AttachmentThumb.d.ts +12 -0
  250. package/dist/primitives/attachment/AttachmentThumb.d.ts.map +1 -0
  251. package/dist/primitives/attachment/AttachmentThumb.js +32 -0
  252. package/dist/primitives/attachment/AttachmentThumb.js.map +1 -0
  253. package/dist/primitives/attachment.d.ts +7 -0
  254. package/dist/primitives/attachment.d.ts.map +1 -0
  255. package/dist/primitives/attachment.js +15 -0
  256. package/dist/primitives/attachment.js.map +1 -0
  257. package/dist/primitives/branchPicker/BranchPickerCount.d.ts +22 -0
  258. package/dist/primitives/branchPicker/BranchPickerCount.d.ts.map +1 -0
  259. package/dist/primitives/branchPicker/BranchPickerCount.js +27 -0
  260. package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -0
  261. package/dist/primitives/branchPicker/BranchPickerNext.d.ts +34 -0
  262. package/dist/primitives/branchPicker/BranchPickerNext.d.ts.map +1 -0
  263. package/dist/primitives/branchPicker/BranchPickerNext.js +26 -0
  264. package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -0
  265. package/dist/primitives/branchPicker/BranchPickerNumber.d.ts +5 -0
  266. package/dist/primitives/branchPicker/BranchPickerNumber.d.ts.map +1 -0
  267. package/dist/primitives/branchPicker/BranchPickerNumber.js +14 -0
  268. package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -0
  269. package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts +34 -0
  270. package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts.map +1 -0
  271. package/dist/primitives/branchPicker/BranchPickerPrevious.js +26 -0
  272. package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -0
  273. package/dist/primitives/branchPicker/BranchPickerRoot.d.ts +43 -0
  274. package/dist/primitives/branchPicker/BranchPickerRoot.d.ts.map +1 -0
  275. package/dist/primitives/branchPicker/BranchPickerRoot.js +46 -0
  276. package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -0
  277. package/dist/primitives/branchPicker.d.ts +6 -0
  278. package/dist/primitives/branchPicker.d.ts.map +1 -0
  279. package/dist/primitives/branchPicker.js +17 -0
  280. package/dist/primitives/branchPicker.js.map +1 -0
  281. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts +30 -0
  282. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts.map +1 -0
  283. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +49 -0
  284. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -0
  285. package/dist/primitives/chainOfThought/ChainOfThoughtParts.d.ts +2 -0
  286. package/dist/primitives/chainOfThought/ChainOfThoughtParts.d.ts.map +1 -0
  287. package/dist/primitives/chainOfThought/ChainOfThoughtParts.js +2 -0
  288. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts +12 -0
  289. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts.map +1 -0
  290. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js +26 -0
  291. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -0
  292. package/dist/primitives/chainOfThought.d.ts +4 -0
  293. package/dist/primitives/chainOfThought.d.ts.map +1 -0
  294. package/dist/primitives/chainOfThought.js +13 -0
  295. package/dist/primitives/chainOfThought.js.map +1 -0
  296. package/dist/primitives/composer/ComposerAddAttachment.d.ts +26 -0
  297. package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -0
  298. package/dist/primitives/composer/ComposerAddAttachment.js +56 -0
  299. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -0
  300. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts +16 -0
  301. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -0
  302. package/dist/primitives/composer/ComposerAttachmentDropzone.js +66 -0
  303. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -0
  304. package/dist/primitives/composer/ComposerAttachments.d.ts +2 -0
  305. package/dist/primitives/composer/ComposerAttachments.d.ts.map +1 -0
  306. package/dist/primitives/composer/ComposerAttachments.js +2 -0
  307. package/dist/primitives/composer/ComposerCancel.d.ts +37 -0
  308. package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -0
  309. package/dist/primitives/composer/ComposerCancel.js +32 -0
  310. package/dist/primitives/composer/ComposerCancel.js.map +1 -0
  311. package/dist/primitives/composer/ComposerCompactContext.d.ts +7 -0
  312. package/dist/primitives/composer/ComposerCompactContext.d.ts.map +1 -0
  313. package/dist/primitives/composer/ComposerCompactContext.js +8 -0
  314. package/dist/primitives/composer/ComposerCompactContext.js.map +1 -0
  315. package/dist/primitives/composer/ComposerDictate.d.ts +29 -0
  316. package/dist/primitives/composer/ComposerDictate.d.ts.map +1 -0
  317. package/dist/primitives/composer/ComposerDictate.js +25 -0
  318. package/dist/primitives/composer/ComposerDictate.js.map +1 -0
  319. package/dist/primitives/composer/ComposerDictationTranscript.d.ts +27 -0
  320. package/dist/primitives/composer/ComposerDictationTranscript.d.ts.map +1 -0
  321. package/dist/primitives/composer/ComposerDictationTranscript.js +42 -0
  322. package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -0
  323. package/dist/primitives/composer/ComposerIf.d.ts +3 -0
  324. package/dist/primitives/composer/ComposerIf.d.ts.map +1 -0
  325. package/dist/primitives/composer/ComposerIf.js +2 -0
  326. package/dist/primitives/composer/ComposerInput.d.ts +132 -0
  327. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -0
  328. package/dist/primitives/composer/ComposerInput.js +239 -0
  329. package/dist/primitives/composer/ComposerInput.js.map +1 -0
  330. package/dist/primitives/composer/ComposerInputPluginContext.d.ts +40 -0
  331. package/dist/primitives/composer/ComposerInputPluginContext.d.ts.map +1 -0
  332. package/dist/primitives/composer/ComposerInputPluginContext.js +30 -0
  333. package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -0
  334. package/dist/primitives/composer/ComposerQueue.d.ts +2 -0
  335. package/dist/primitives/composer/ComposerQueue.d.ts.map +1 -0
  336. package/dist/primitives/composer/ComposerQueue.js +2 -0
  337. package/dist/primitives/composer/ComposerQuote.d.ts +58 -0
  338. package/dist/primitives/composer/ComposerQuote.d.ts.map +1 -0
  339. package/dist/primitives/composer/ComposerQuote.js +95 -0
  340. package/dist/primitives/composer/ComposerQuote.js.map +1 -0
  341. package/dist/primitives/composer/ComposerResume.d.ts +27 -0
  342. package/dist/primitives/composer/ComposerResume.d.ts.map +1 -0
  343. package/dist/primitives/composer/ComposerResume.js +24 -0
  344. package/dist/primitives/composer/ComposerResume.js.map +1 -0
  345. package/dist/primitives/composer/ComposerRoot.d.ts +56 -0
  346. package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -0
  347. package/dist/primitives/composer/ComposerRoot.js +96 -0
  348. package/dist/primitives/composer/ComposerRoot.js.map +1 -0
  349. package/dist/primitives/composer/ComposerSend.d.ts +34 -0
  350. package/dist/primitives/composer/ComposerSend.d.ts.map +1 -0
  351. package/dist/primitives/composer/ComposerSend.js +28 -0
  352. package/dist/primitives/composer/ComposerSend.js.map +1 -0
  353. package/dist/primitives/composer/ComposerStopDictation.d.ts +29 -0
  354. package/dist/primitives/composer/ComposerStopDictation.d.ts.map +1 -0
  355. package/dist/primitives/composer/ComposerStopDictation.js +29 -0
  356. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -0
  357. package/dist/primitives/composer/composerInputSelector.d.ts +3 -0
  358. package/dist/primitives/composer/composerInputSelector.d.ts.map +1 -0
  359. package/dist/primitives/composer/composerInputSelector.js +6 -0
  360. package/dist/primitives/composer/composerInputSelector.js.map +1 -0
  361. package/dist/primitives/composer/trigger/TriggerPopover.d.ts +63 -0
  362. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -0
  363. package/dist/primitives/composer/trigger/TriggerPopover.js +161 -0
  364. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -0
  365. package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts +30 -0
  366. package/dist/primitives/composer/trigger/TriggerPopoverAction.d.ts.map +1 -0
  367. package/dist/primitives/composer/trigger/TriggerPopoverAction.js +49 -0
  368. package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -0
  369. package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts +16 -0
  370. package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts.map +1 -0
  371. package/dist/primitives/composer/trigger/TriggerPopoverBack.js +34 -0
  372. package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -0
  373. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +34 -0
  374. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts.map +1 -0
  375. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +70 -0
  376. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -0
  377. package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts +27 -0
  378. package/dist/primitives/composer/trigger/TriggerPopoverDirective.d.ts.map +1 -0
  379. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +41 -0
  380. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -0
  381. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +36 -0
  382. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts.map +1 -0
  383. package/dist/primitives/composer/trigger/TriggerPopoverItems.js +71 -0
  384. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -0
  385. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts +48 -0
  386. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -0
  387. package/dist/primitives/composer/trigger/TriggerPopoverResource.js +67 -0
  388. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -0
  389. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +58 -0
  390. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts.map +1 -0
  391. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +128 -0
  392. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -0
  393. package/dist/primitives/composer/trigger/detectTrigger.d.ts +18 -0
  394. package/dist/primitives/composer/trigger/detectTrigger.d.ts.map +1 -0
  395. package/dist/primitives/composer/trigger/detectTrigger.js +39 -0
  396. package/dist/primitives/composer/trigger/detectTrigger.js.map +1 -0
  397. package/dist/primitives/composer/trigger/index.d.ts +20 -0
  398. package/dist/primitives/composer/trigger/index.d.ts.map +1 -0
  399. package/dist/primitives/composer/trigger/index.js +15 -0
  400. package/dist/primitives/composer/trigger/index.js.map +1 -0
  401. package/dist/primitives/composer/trigger/matchesTriggerItemQuery.d.ts +3 -0
  402. package/dist/primitives/composer/trigger/matchesTriggerItemQuery.d.ts.map +1 -0
  403. package/dist/primitives/composer/trigger/matchesTriggerItemQuery.js +8 -0
  404. package/dist/primitives/composer/trigger/matchesTriggerItemQuery.js.map +1 -0
  405. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts +17 -0
  406. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -0
  407. package/dist/primitives/composer/trigger/triggerDetectionResource.js +25 -0
  408. package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -0
  409. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts +30 -0
  410. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -0
  411. package/dist/primitives/composer/trigger/triggerKeyboardResource.js +85 -0
  412. package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -0
  413. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts +23 -0
  414. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -0
  415. package/dist/primitives/composer/trigger/triggerNavigationResource.js +88 -0
  416. package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -0
  417. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts +37 -0
  418. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -0
  419. package/dist/primitives/composer/trigger/triggerSelectionResource.js +52 -0
  420. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -0
  421. package/dist/primitives/composer/useComposerCancelWithFocus.d.ts +2 -0
  422. package/dist/primitives/composer/useComposerCancelWithFocus.d.ts.map +1 -0
  423. package/dist/primitives/composer/useComposerCancelWithFocus.js +31 -0
  424. package/dist/primitives/composer/useComposerCancelWithFocus.js.map +1 -0
  425. package/dist/primitives/composer/useComposerInputState.d.ts +11 -0
  426. package/dist/primitives/composer/useComposerInputState.d.ts.map +1 -0
  427. package/dist/primitives/composer/useComposerInputState.js +33 -0
  428. package/dist/primitives/composer/useComposerInputState.js.map +1 -0
  429. package/dist/primitives/composer/useTextareaAutosize.d.ts +13 -0
  430. package/dist/primitives/composer/useTextareaAutosize.d.ts.map +1 -0
  431. package/dist/primitives/composer/useTextareaAutosize.js +113 -0
  432. package/dist/primitives/composer/useTextareaAutosize.js.map +1 -0
  433. package/dist/primitives/composer.d.ts +73 -0
  434. package/dist/primitives/composer.d.ts.map +1 -0
  435. package/dist/primitives/composer.js +64 -0
  436. package/dist/primitives/composer.js.map +1 -0
  437. package/dist/primitives/dropdownMenuRenderPrimitives.d.ts +173 -0
  438. package/dist/primitives/dropdownMenuRenderPrimitives.d.ts.map +1 -0
  439. package/dist/primitives/dropdownMenuRenderPrimitives.js +10 -0
  440. package/dist/primitives/dropdownMenuRenderPrimitives.js.map +1 -0
  441. package/dist/primitives/error/ErrorMessage.d.ts +12 -0
  442. package/dist/primitives/error/ErrorMessage.d.ts.map +1 -0
  443. package/dist/primitives/error/ErrorMessage.js +27 -0
  444. package/dist/primitives/error/ErrorMessage.js.map +1 -0
  445. package/dist/primitives/error/ErrorRoot.d.ts +12 -0
  446. package/dist/primitives/error/ErrorRoot.d.ts.map +1 -0
  447. package/dist/primitives/error/ErrorRoot.js +27 -0
  448. package/dist/primitives/error/ErrorRoot.js.map +1 -0
  449. package/dist/primitives/error.d.ts +3 -0
  450. package/dist/primitives/error.d.ts.map +1 -0
  451. package/dist/primitives/error.js +11 -0
  452. package/dist/primitives/error.js.map +1 -0
  453. package/dist/primitives/message/MessageAttachments.d.ts +2 -0
  454. package/dist/primitives/message/MessageAttachments.d.ts.map +1 -0
  455. package/dist/primitives/message/MessageAttachments.js +2 -0
  456. package/dist/primitives/message/MessageError.d.ts +7 -0
  457. package/dist/primitives/message/MessageError.d.ts.map +1 -0
  458. package/dist/primitives/message/MessageError.js +14 -0
  459. package/dist/primitives/message/MessageError.js.map +1 -0
  460. package/dist/primitives/message/MessageIf.d.ts +27 -0
  461. package/dist/primitives/message/MessageIf.d.ts.map +1 -0
  462. package/dist/primitives/message/MessageIf.js +54 -0
  463. package/dist/primitives/message/MessageIf.js.map +1 -0
  464. package/dist/primitives/message/MessageParts.d.ts +7 -0
  465. package/dist/primitives/message/MessageParts.d.ts.map +1 -0
  466. package/dist/primitives/message/MessageParts.js +40 -0
  467. package/dist/primitives/message/MessageParts.js.map +1 -0
  468. package/dist/primitives/message/MessagePartsGrouped.d.ts +153 -0
  469. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -0
  470. package/dist/primitives/message/MessagePartsGrouped.js +114 -0
  471. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -0
  472. package/dist/primitives/message/MessageRoot.d.ts +35 -0
  473. package/dist/primitives/message/MessageRoot.d.ts.map +1 -0
  474. package/dist/primitives/message/MessageRoot.js +104 -0
  475. package/dist/primitives/message/MessageRoot.js.map +1 -0
  476. package/dist/primitives/message.d.ts +13 -0
  477. package/dist/primitives/message.d.ts.map +1 -0
  478. package/dist/primitives/message.js +27 -0
  479. package/dist/primitives/message.js.map +1 -0
  480. package/dist/primitives/messagePart/MessagePartImage.d.ts +12 -0
  481. package/dist/primitives/messagePart/MessagePartImage.d.ts.map +1 -0
  482. package/dist/primitives/messagePart/MessagePartImage.js +27 -0
  483. package/dist/primitives/messagePart/MessagePartImage.js.map +1 -0
  484. package/dist/primitives/messagePart/MessagePartInProgress.d.ts +2 -0
  485. package/dist/primitives/messagePart/MessagePartInProgress.d.ts.map +1 -0
  486. package/dist/primitives/messagePart/MessagePartInProgress.js +2 -0
  487. package/dist/primitives/messagePart/MessagePartText.d.ts +19 -0
  488. package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -0
  489. package/dist/primitives/messagePart/MessagePartText.js +33 -0
  490. package/dist/primitives/messagePart/MessagePartText.js.map +1 -0
  491. package/dist/primitives/messagePart/useMessagePartData.d.ts +18 -0
  492. package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -0
  493. package/dist/primitives/messagePart/useMessagePartData.js +32 -0
  494. package/dist/primitives/messagePart/useMessagePartData.js.map +1 -0
  495. package/dist/primitives/messagePart/useMessagePartFile.d.ts +19 -0
  496. package/dist/primitives/messagePart/useMessagePartFile.d.ts.map +1 -0
  497. package/dist/primitives/messagePart/useMessagePartFile.js +33 -0
  498. package/dist/primitives/messagePart/useMessagePartFile.js.map +1 -0
  499. package/dist/primitives/messagePart/useMessagePartImage.d.ts +19 -0
  500. package/dist/primitives/messagePart/useMessagePartImage.d.ts.map +1 -0
  501. package/dist/primitives/messagePart/useMessagePartImage.js +32 -0
  502. package/dist/primitives/messagePart/useMessagePartImage.js.map +1 -0
  503. package/dist/primitives/messagePart/useMessagePartReasoning.d.ts +19 -0
  504. package/dist/primitives/messagePart/useMessagePartReasoning.d.ts.map +1 -0
  505. package/dist/primitives/messagePart/useMessagePartReasoning.js +32 -0
  506. package/dist/primitives/messagePart/useMessagePartReasoning.js.map +1 -0
  507. package/dist/primitives/messagePart/useMessagePartSource.d.ts +39 -0
  508. package/dist/primitives/messagePart/useMessagePartSource.d.ts.map +1 -0
  509. package/dist/primitives/messagePart/useMessagePartSource.js +34 -0
  510. package/dist/primitives/messagePart/useMessagePartSource.js.map +1 -0
  511. package/dist/primitives/messagePart/useMessagePartText.d.ts +21 -0
  512. package/dist/primitives/messagePart/useMessagePartText.d.ts.map +1 -0
  513. package/dist/primitives/messagePart/useMessagePartText.js +32 -0
  514. package/dist/primitives/messagePart/useMessagePartText.js.map +1 -0
  515. package/dist/primitives/messagePart.d.ts +5 -0
  516. package/dist/primitives/messagePart.d.ts.map +1 -0
  517. package/dist/primitives/messagePart.js +15 -0
  518. package/dist/primitives/messagePart.js.map +1 -0
  519. package/dist/primitives/queueItem/QueueItemRemove.d.ts +25 -0
  520. package/dist/primitives/queueItem/QueueItemRemove.d.ts.map +1 -0
  521. package/dist/primitives/queueItem/QueueItemRemove.js +23 -0
  522. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -0
  523. package/dist/primitives/queueItem/QueueItemSteer.d.ts +25 -0
  524. package/dist/primitives/queueItem/QueueItemSteer.d.ts.map +1 -0
  525. package/dist/primitives/queueItem/QueueItemSteer.js +23 -0
  526. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -0
  527. package/dist/primitives/queueItem/QueueItemText.d.ts +20 -0
  528. package/dist/primitives/queueItem/QueueItemText.d.ts.map +1 -0
  529. package/dist/primitives/queueItem/QueueItemText.js +35 -0
  530. package/dist/primitives/queueItem/QueueItemText.js.map +1 -0
  531. package/dist/primitives/queueItem.d.ts +4 -0
  532. package/dist/primitives/queueItem.d.ts.map +1 -0
  533. package/dist/primitives/queueItem.js +13 -0
  534. package/dist/primitives/queueItem.js.map +1 -0
  535. package/dist/primitives/reasoning/useScrollLock.d.ts +29 -0
  536. package/dist/primitives/reasoning/useScrollLock.d.ts.map +1 -0
  537. package/dist/primitives/reasoning/useScrollLock.js +108 -0
  538. package/dist/primitives/reasoning/useScrollLock.js.map +1 -0
  539. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts +13 -0
  540. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts.map +1 -0
  541. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +43 -0
  542. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -0
  543. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts +20 -0
  544. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -0
  545. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +147 -0
  546. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -0
  547. package/dist/primitives/selectionToolbar.d.ts +3 -0
  548. package/dist/primitives/selectionToolbar.d.ts.map +1 -0
  549. package/dist/primitives/selectionToolbar.js +11 -0
  550. package/dist/primitives/selectionToolbar.js.map +1 -0
  551. package/dist/primitives/suggestion/SuggestionDescription.d.ts +13 -0
  552. package/dist/primitives/suggestion/SuggestionDescription.d.ts.map +1 -0
  553. package/dist/primitives/suggestion/SuggestionDescription.js +28 -0
  554. package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -0
  555. package/dist/primitives/suggestion/SuggestionTitle.d.ts +20 -0
  556. package/dist/primitives/suggestion/SuggestionTitle.d.ts.map +1 -0
  557. package/dist/primitives/suggestion/SuggestionTitle.js +35 -0
  558. package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -0
  559. package/dist/primitives/suggestion/SuggestionTrigger.d.ts +72 -0
  560. package/dist/primitives/suggestion/SuggestionTrigger.d.ts.map +1 -0
  561. package/dist/primitives/suggestion/SuggestionTrigger.js +29 -0
  562. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -0
  563. package/dist/primitives/suggestion.d.ts +4 -0
  564. package/dist/primitives/suggestion.d.ts.map +1 -0
  565. package/dist/primitives/suggestion.js +13 -0
  566. package/dist/primitives/suggestion.js.map +1 -0
  567. package/dist/primitives/thread/ThreadEmpty.d.ts +11 -0
  568. package/dist/primitives/thread/ThreadEmpty.d.ts.map +1 -0
  569. package/dist/primitives/thread/ThreadEmpty.js +17 -0
  570. package/dist/primitives/thread/ThreadEmpty.js.map +1 -0
  571. package/dist/primitives/thread/ThreadIf.d.ts +19 -0
  572. package/dist/primitives/thread/ThreadIf.d.ts.map +1 -0
  573. package/dist/primitives/thread/ThreadIf.js +33 -0
  574. package/dist/primitives/thread/ThreadIf.js.map +1 -0
  575. package/dist/primitives/thread/ThreadLoadEarlier.d.ts +23 -0
  576. package/dist/primitives/thread/ThreadLoadEarlier.d.ts.map +1 -0
  577. package/dist/primitives/thread/ThreadLoadEarlier.js +23 -0
  578. package/dist/primitives/thread/ThreadLoadEarlier.js.map +1 -0
  579. package/dist/primitives/thread/ThreadMessages.d.ts +2 -0
  580. package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -0
  581. package/dist/primitives/thread/ThreadMessages.js +2 -0
  582. package/dist/primitives/thread/ThreadRoot.d.ts +37 -0
  583. package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -0
  584. package/dist/primitives/thread/ThreadRoot.js +84 -0
  585. package/dist/primitives/thread/ThreadRoot.js.map +1 -0
  586. package/dist/primitives/thread/ThreadRootElementContext.d.ts +4 -0
  587. package/dist/primitives/thread/ThreadRootElementContext.d.ts.map +1 -0
  588. package/dist/primitives/thread/ThreadRootElementContext.js +8 -0
  589. package/dist/primitives/thread/ThreadRootElementContext.js.map +1 -0
  590. package/dist/primitives/thread/ThreadScrollToBottom.d.ts +22 -0
  591. package/dist/primitives/thread/ThreadScrollToBottom.d.ts.map +1 -0
  592. package/dist/primitives/thread/ThreadScrollToBottom.js +17 -0
  593. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -0
  594. package/dist/primitives/thread/ThreadSuggestion.d.ts +80 -0
  595. package/dist/primitives/thread/ThreadSuggestion.d.ts.map +1 -0
  596. package/dist/primitives/thread/ThreadSuggestion.js +23 -0
  597. package/dist/primitives/thread/ThreadSuggestion.js.map +1 -0
  598. package/dist/primitives/thread/ThreadSuggestions.d.ts +2 -0
  599. package/dist/primitives/thread/ThreadSuggestions.d.ts.map +1 -0
  600. package/dist/primitives/thread/ThreadSuggestions.js +2 -0
  601. package/dist/primitives/thread/ThreadViewport.d.ts +115 -0
  602. package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -0
  603. package/dist/primitives/thread/ThreadViewport.js +96 -0
  604. package/dist/primitives/thread/ThreadViewport.js.map +1 -0
  605. package/dist/primitives/thread/ThreadViewportFooter.d.ts +13 -0
  606. package/dist/primitives/thread/ThreadViewportFooter.d.ts.map +1 -0
  607. package/dist/primitives/thread/ThreadViewportFooter.js +39 -0
  608. package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -0
  609. package/dist/primitives/thread/topAnchor/computeTopAnchorSlack.d.ts +22 -0
  610. package/dist/primitives/thread/topAnchor/computeTopAnchorSlack.d.ts.map +1 -0
  611. package/dist/primitives/thread/topAnchor/computeTopAnchorSlack.js +51 -0
  612. package/dist/primitives/thread/topAnchor/computeTopAnchorSlack.js.map +1 -0
  613. package/dist/primitives/thread/topAnchor/createReserveObservers.d.ts +5 -0
  614. package/dist/primitives/thread/topAnchor/createReserveObservers.d.ts.map +1 -0
  615. package/dist/primitives/thread/topAnchor/createReserveObservers.js +37 -0
  616. package/dist/primitives/thread/topAnchor/createReserveObservers.js.map +1 -0
  617. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts +26 -0
  618. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.d.ts.map +1 -0
  619. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js +139 -0
  620. package/dist/primitives/thread/topAnchor/mountTopAnchorReserve.js.map +1 -0
  621. package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts +27 -0
  622. package/dist/primitives/thread/topAnchor/topAnchorTurn.d.ts.map +1 -0
  623. package/dist/primitives/thread/topAnchor/topAnchorTurn.js +30 -0
  624. package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -0
  625. package/dist/primitives/thread/topAnchor/topAnchorUtils.d.ts +15 -0
  626. package/dist/primitives/thread/topAnchor/topAnchorUtils.d.ts.map +1 -0
  627. package/dist/primitives/thread/topAnchor/topAnchorUtils.js +46 -0
  628. package/dist/primitives/thread/topAnchor/topAnchorUtils.js.map +1 -0
  629. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.d.ts +8 -0
  630. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.d.ts.map +1 -0
  631. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +21 -0
  632. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -0
  633. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts +32 -0
  634. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -0
  635. package/dist/primitives/thread/useThreadViewportAutoScroll.js +74 -0
  636. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -0
  637. package/dist/primitives/thread.d.ts +15 -0
  638. package/dist/primitives/thread.d.ts.map +1 -0
  639. package/dist/primitives/thread.js +33 -0
  640. package/dist/primitives/thread.js.map +1 -0
  641. package/dist/primitives/threadList/ThreadListItems.d.ts +2 -0
  642. package/dist/primitives/threadList/ThreadListItems.d.ts.map +1 -0
  643. package/dist/primitives/threadList/ThreadListItems.js +2 -0
  644. package/dist/primitives/threadList/ThreadListLoadMore.d.ts +17 -0
  645. package/dist/primitives/threadList/ThreadListLoadMore.d.ts.map +1 -0
  646. package/dist/primitives/threadList/ThreadListLoadMore.js +13 -0
  647. package/dist/primitives/threadList/ThreadListLoadMore.js.map +1 -0
  648. package/dist/primitives/threadList/ThreadListNew.d.ts +11 -0
  649. package/dist/primitives/threadList/ThreadListNew.d.ts.map +1 -0
  650. package/dist/primitives/threadList/ThreadListNew.js +29 -0
  651. package/dist/primitives/threadList/ThreadListNew.js.map +1 -0
  652. package/dist/primitives/threadList/ThreadListRoot.d.ts +12 -0
  653. package/dist/primitives/threadList/ThreadListRoot.d.ts.map +1 -0
  654. package/dist/primitives/threadList/ThreadListRoot.js +25 -0
  655. package/dist/primitives/threadList/ThreadListRoot.js.map +1 -0
  656. package/dist/primitives/threadList.d.ts +6 -0
  657. package/dist/primitives/threadList.d.ts.map +1 -0
  658. package/dist/primitives/threadList.js +16 -0
  659. package/dist/primitives/threadList.js.map +1 -0
  660. package/dist/primitives/threadListFocusGroup.d.ts +28 -0
  661. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -0
  662. package/dist/primitives/threadListFocusGroup.js +64 -0
  663. package/dist/primitives/threadListFocusGroup.js.map +1 -0
  664. package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts +17 -0
  665. package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts.map +1 -0
  666. package/dist/primitives/threadListItem/ThreadListItemArchive.js +12 -0
  667. package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -0
  668. package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts +17 -0
  669. package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts.map +1 -0
  670. package/dist/primitives/threadListItem/ThreadListItemDelete.js +12 -0
  671. package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -0
  672. package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts +12 -0
  673. package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts.map +1 -0
  674. package/dist/primitives/threadListItem/ThreadListItemRoot.js +61 -0
  675. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -0
  676. package/dist/primitives/threadListItem/ThreadListItemTitle.d.ts +2 -0
  677. package/dist/primitives/threadListItem/ThreadListItemTitle.d.ts.map +1 -0
  678. package/dist/primitives/threadListItem/ThreadListItemTitle.js +2 -0
  679. package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts +13 -0
  680. package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts.map +1 -0
  681. package/dist/primitives/threadListItem/ThreadListItemTrigger.js +35 -0
  682. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -0
  683. package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts +17 -0
  684. package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -0
  685. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js +13 -0
  686. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -0
  687. package/dist/primitives/threadListItem.d.ts +7 -0
  688. package/dist/primitives/threadListItem.d.ts.map +1 -0
  689. package/dist/primitives/threadListItem.js +19 -0
  690. package/dist/primitives/threadListItem.js.map +1 -0
  691. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts +47 -0
  692. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.d.ts.map +1 -0
  693. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +48 -0
  694. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -0
  695. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.d.ts +19 -0
  696. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.d.ts.map +1 -0
  697. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js +24 -0
  698. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -0
  699. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts +19 -0
  700. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.d.ts.map +1 -0
  701. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +45 -0
  702. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -0
  703. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.d.ts +16 -0
  704. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.d.ts.map +1 -0
  705. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js +24 -0
  706. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -0
  707. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts +17 -0
  708. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.d.ts.map +1 -0
  709. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +42 -0
  710. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -0
  711. package/dist/primitives/threadListItemMore/scope.d.ts +5 -0
  712. package/dist/primitives/threadListItemMore/scope.d.ts.map +1 -0
  713. package/dist/primitives/threadListItemMore/scope.js +7 -0
  714. package/dist/primitives/threadListItemMore/scope.js.map +1 -0
  715. package/dist/primitives/threadListItemMore.d.ts +6 -0
  716. package/dist/primitives/threadListItemMore.d.ts.map +1 -0
  717. package/dist/primitives/threadListItemMore.js +17 -0
  718. package/dist/primitives/threadListItemMore.js.map +1 -0
  719. package/dist/sandbox-host/SandboxHost.d.ts +38 -0
  720. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -0
  721. package/dist/sandbox-host/SandboxHost.js +146 -0
  722. package/dist/sandbox-host/SandboxHost.js.map +1 -0
  723. package/dist/unstable/useComposerInput.d.ts +87 -0
  724. package/dist/unstable/useComposerInput.d.ts.map +1 -0
  725. package/dist/unstable/useComposerInput.js +77 -0
  726. package/dist/unstable/useComposerInput.js.map +1 -0
  727. package/dist/unstable/useComposerInputHistory.d.ts +27 -0
  728. package/dist/unstable/useComposerInputHistory.d.ts.map +1 -0
  729. package/dist/unstable/useComposerInputHistory.js +125 -0
  730. package/dist/unstable/useComposerInputHistory.js.map +1 -0
  731. package/dist/unstable/useLiveCompletionAdapter.d.ts +52 -0
  732. package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -0
  733. package/dist/unstable/useLiveCompletionAdapter.js +184 -0
  734. package/dist/unstable/useLiveCompletionAdapter.js.map +1 -0
  735. package/dist/unstable/useMentionAdapter.d.ts +95 -0
  736. package/dist/unstable/useMentionAdapter.d.ts.map +1 -0
  737. package/dist/unstable/useMentionAdapter.js +129 -0
  738. package/dist/unstable/useMentionAdapter.js.map +1 -0
  739. package/dist/unstable/useMessageStallDetection.d.ts +30 -0
  740. package/dist/unstable/useMessageStallDetection.d.ts.map +1 -0
  741. package/dist/unstable/useMessageStallDetection.js +92 -0
  742. package/dist/unstable/useMessageStallDetection.js.map +1 -0
  743. package/dist/unstable/useModelContextSnapshot.d.ts +32 -0
  744. package/dist/unstable/useModelContextSnapshot.d.ts.map +1 -0
  745. package/dist/unstable/useModelContextSnapshot.js +46 -0
  746. package/dist/unstable/useModelContextSnapshot.js.map +1 -0
  747. package/dist/unstable/useSlashCommandAdapter.d.ts +53 -0
  748. package/dist/unstable/useSlashCommandAdapter.d.ts.map +1 -0
  749. package/dist/unstable/useSlashCommandAdapter.js +84 -0
  750. package/dist/unstable/useSlashCommandAdapter.js.map +1 -0
  751. package/dist/unstable/webmcp/WebMcpRegistrationResource.d.ts +12 -0
  752. package/dist/unstable/webmcp/WebMcpRegistrationResource.d.ts.map +1 -0
  753. package/dist/unstable/webmcp/WebMcpRegistrationResource.js +47 -0
  754. package/dist/unstable/webmcp/WebMcpRegistrationResource.js.map +1 -0
  755. package/dist/unstable/webmcp/convertTools.d.ts +18 -0
  756. package/dist/unstable/webmcp/convertTools.d.ts.map +1 -0
  757. package/dist/unstable/webmcp/convertTools.js +160 -0
  758. package/dist/unstable/webmcp/convertTools.js.map +1 -0
  759. package/dist/unstable/webmcp/useWebMcpProvider.d.ts +23 -0
  760. package/dist/unstable/webmcp/useWebMcpProvider.d.ts.map +1 -0
  761. package/dist/unstable/webmcp/useWebMcpProvider.js +111 -0
  762. package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -0
  763. package/dist/unstable/webmcp/webmcp-host.d.ts +36 -0
  764. package/dist/unstable/webmcp/webmcp-host.d.ts.map +1 -0
  765. package/dist/unstable/webmcp/webmcp-host.js +28 -0
  766. package/dist/unstable/webmcp/webmcp-host.js.map +1 -0
  767. package/dist/utils/Primitive.d.ts +28 -0
  768. package/dist/utils/Primitive.d.ts.map +1 -0
  769. package/dist/utils/Primitive.js +229 -0
  770. package/dist/utils/Primitive.js.map +1 -0
  771. package/dist/utils/RequireAtLeastOne.d.ts +4 -0
  772. package/dist/utils/RequireAtLeastOne.d.ts.map +1 -0
  773. package/dist/utils/RequireAtLeastOne.js +0 -0
  774. package/dist/utils/createActionButton.d.ts +18 -0
  775. package/dist/utils/createActionButton.d.ts.map +1 -0
  776. package/dist/utils/createActionButton.js +39 -0
  777. package/dist/utils/createActionButton.js.map +1 -0
  778. package/dist/utils/getSelectionMessageId.d.ts +2 -0
  779. package/dist/utils/getSelectionMessageId.d.ts.map +1 -0
  780. package/dist/utils/getSelectionMessageId.js +84 -0
  781. package/dist/utils/getSelectionMessageId.js.map +1 -0
  782. package/dist/utils/hooks/useManagedRef.d.ts +2 -0
  783. package/dist/utils/hooks/useManagedRef.d.ts.map +1 -0
  784. package/dist/utils/hooks/useManagedRef.js +15 -0
  785. package/dist/utils/hooks/useManagedRef.js.map +1 -0
  786. package/dist/utils/hooks/useMediaQuery.d.ts +3 -0
  787. package/dist/utils/hooks/useMediaQuery.d.ts.map +1 -0
  788. package/dist/utils/hooks/useMediaQuery.js +33 -0
  789. package/dist/utils/hooks/useMediaQuery.js.map +1 -0
  790. package/dist/utils/hooks/useOnScrollToBottom.d.ts +7 -0
  791. package/dist/utils/hooks/useOnScrollToBottom.d.ts.map +1 -0
  792. package/dist/utils/hooks/useOnScrollToBottom.js +15 -0
  793. package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -0
  794. package/dist/utils/hooks/useSizeHandle.d.ts +12 -0
  795. package/dist/utils/hooks/useSizeHandle.d.ts.map +1 -0
  796. package/dist/utils/hooks/useSizeHandle.js +33 -0
  797. package/dist/utils/hooks/useSizeHandle.js.map +1 -0
  798. package/dist/utils/isCompositionKey.d.ts +2 -0
  799. package/dist/utils/isCompositionKey.d.ts.map +1 -0
  800. package/dist/utils/isCompositionKey.js +5 -0
  801. package/dist/utils/isCompositionKey.js.map +1 -0
  802. package/dist/utils/smooth/SmoothContext.d.ts +117 -0
  803. package/dist/utils/smooth/SmoothContext.d.ts.map +1 -0
  804. package/dist/utils/smooth/SmoothContext.js +40 -0
  805. package/dist/utils/smooth/SmoothContext.js.map +1 -0
  806. package/dist/utils/smooth/useSmooth.d.ts +63 -0
  807. package/dist/utils/smooth/useSmooth.d.ts.map +1 -0
  808. package/dist/utils/smooth/useSmooth.js +144 -0
  809. package/dist/utils/smooth/useSmooth.js.map +1 -0
  810. package/dist/utils/useIsomorphicLayoutEffect.d.ts +2 -0
  811. package/dist/utils/useIsomorphicLayoutEffect.d.ts.map +1 -0
  812. package/dist/utils/useIsomorphicLayoutEffect.js +1 -0
  813. package/package.json +115 -3
  814. package/src/adapters/OpenAIRealtimeVoiceAdapter.test.ts +761 -0
  815. package/src/adapters/OpenAIRealtimeVoiceAdapter.ts +388 -0
  816. package/src/assistant-transport.ts +98 -0
  817. package/src/augmentations.ts +35 -0
  818. package/src/context/ReadonlyStore.ts +7 -0
  819. package/src/context/index.ts +23 -0
  820. package/src/context/providers/AttachmentByIndexProvider.tsx +6 -0
  821. package/src/context/providers/ChainOfThoughtByIndicesProvider.tsx +3 -0
  822. package/src/context/providers/MessageByIndexProvider.tsx +3 -0
  823. package/src/context/providers/MessageProvider.tsx +34 -0
  824. package/src/context/providers/PartByIndexProvider.tsx +3 -0
  825. package/src/context/providers/SuggestionByIndexProvider.tsx +6 -0
  826. package/src/context/providers/TextMessagePartProvider.tsx +3 -0
  827. package/src/context/providers/ThreadListItemProvider.tsx +6 -0
  828. package/src/context/providers/ThreadViewportProvider.tsx +49 -0
  829. package/src/context/providers/vue-providers.test.tsx +326 -0
  830. package/src/context/stores/ThreadViewport.test.ts +29 -0
  831. package/src/context/stores/ThreadViewport.ts +278 -0
  832. package/src/context/vue/ThreadViewportContext.ts +22 -0
  833. package/src/context/vue/utils/createContextHook.ts +26 -0
  834. package/src/context/vue/utils/createContextStoreHook.ts +93 -0
  835. package/src/devtools/DevToolsHooks.test.ts +40 -0
  836. package/src/devtools/DevToolsHooks.ts +142 -0
  837. package/src/hooks/message-readers.test.ts +128 -0
  838. package/src/hooks/useMessageQuote.ts +25 -0
  839. package/src/hooks/useMessageTiming.ts +26 -0
  840. package/src/hooks/useToolCallElapsed.test.tsx +172 -0
  841. package/src/hooks/useToolCallElapsed.ts +65 -0
  842. package/src/index.ts +623 -0
  843. package/src/internal.ts +33 -0
  844. package/src/legacy-runtime/AssistantRuntimeProvider.tsx +61 -0
  845. package/src/mcp-apps/McpAppRenderer.composer.test.tsx +129 -0
  846. package/src/mcp-apps/McpAppRenderer.test.tsx +906 -0
  847. package/src/mcp-apps/McpAppRenderer.tsx +411 -0
  848. package/src/mcp-apps/McpAppsRemoteHost.concurrent.test.tsx +226 -0
  849. package/src/mcp-apps/McpAppsRemoteHost.test.ts +152 -0
  850. package/src/mcp-apps/McpAppsRemoteHost.ts +160 -0
  851. package/src/mcp-apps/app-frame.test.tsx +648 -0
  852. package/src/mcp-apps/app-frame.tsx +351 -0
  853. package/src/mcp-apps/bridge.test.ts +836 -0
  854. package/src/mcp-apps/bridge.ts +538 -0
  855. package/src/mcp-apps/csp.test.ts +112 -0
  856. package/src/mcp-apps/csp.ts +124 -0
  857. package/src/mcp-apps/index.ts +23 -0
  858. package/src/mcp-apps/types.ts +176 -0
  859. package/src/mcp-apps/utils.ts +23 -0
  860. package/src/model-context/frame/SPEC_AssistantFrame.md +105 -0
  861. package/src/model-context/frame/useAssistantFrameHost.detach.test.tsx +155 -0
  862. package/src/model-context/frame/useAssistantFrameHost.reload.test.tsx +116 -0
  863. package/src/model-context/frame/useAssistantFrameHost.test.tsx +43 -0
  864. package/src/model-context/frame/useAssistantFrameHost.ts +137 -0
  865. package/src/model-context/makeAssistantVisible.test.tsx +460 -0
  866. package/src/model-context/makeAssistantVisible.tsx +184 -0
  867. package/src/model-context/registry/SPEC_ModelContextRegistry.md +40 -0
  868. package/src/primitives/actionBar/ActionBarCopy.test.tsx +28 -0
  869. package/src/primitives/actionBar/ActionBarCopy.tsx +100 -0
  870. package/src/primitives/actionBar/ActionBarEdit.ts +63 -0
  871. package/src/primitives/actionBar/ActionBarExportMarkdown.test.tsx +83 -0
  872. package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +65 -0
  873. package/src/primitives/actionBar/ActionBarFeedback.test.tsx +59 -0
  874. package/src/primitives/actionBar/ActionBarFeedbackNegative.tsx +48 -0
  875. package/src/primitives/actionBar/ActionBarFeedbackPositive.tsx +48 -0
  876. package/src/primitives/actionBar/ActionBarInteractionContext.ts +13 -0
  877. package/src/primitives/actionBar/ActionBarReload.ts +64 -0
  878. package/src/primitives/actionBar/ActionBarRoot.tsx +119 -0
  879. package/src/primitives/actionBar/ActionBarSpeak.ts +25 -0
  880. package/src/primitives/actionBar/ActionBarStopSpeaking.tsx +24 -0
  881. package/src/primitives/actionBar/useActionBarFloatStatus.ts +48 -0
  882. package/src/primitives/actionBar.ts +9 -0
  883. package/src/primitives/actionBarMore/ActionBarMoreContent.tsx +45 -0
  884. package/src/primitives/actionBarMore/ActionBarMoreItem.tsx +22 -0
  885. package/src/primitives/actionBarMore/ActionBarMoreRoot.test.ts +70 -0
  886. package/src/primitives/actionBarMore/ActionBarMoreRoot.tsx +46 -0
  887. package/src/primitives/actionBarMore/ActionBarMoreSeparator.tsx +26 -0
  888. package/src/primitives/actionBarMore/ActionBarMoreTrigger.tsx +26 -0
  889. package/src/primitives/actionBarMore/scope.ts +6 -0
  890. package/src/primitives/actionBarMore.ts +5 -0
  891. package/src/primitives/assistantModal/AssistantModalAnchor.tsx +22 -0
  892. package/src/primitives/assistantModal/AssistantModalContent.tsx +85 -0
  893. package/src/primitives/assistantModal/AssistantModalRoot.test.tsx +153 -0
  894. package/src/primitives/assistantModal/AssistantModalRoot.tsx +48 -0
  895. package/src/primitives/assistantModal/AssistantModalTrigger.test.tsx +65 -0
  896. package/src/primitives/assistantModal/AssistantModalTrigger.tsx +22 -0
  897. package/src/primitives/assistantModal/popoverRenderPrimitives.ts +6 -0
  898. package/src/primitives/assistantModal/scope.ts +6 -0
  899. package/src/primitives/assistantModal.ts +4 -0
  900. package/src/primitives/attachment/AttachmentName.tsx +16 -0
  901. package/src/primitives/attachment/AttachmentRemove.ts +28 -0
  902. package/src/primitives/attachment/AttachmentRoot.tsx +52 -0
  903. package/src/primitives/attachment/AttachmentThumb.test.tsx +179 -0
  904. package/src/primitives/attachment/AttachmentThumb.tsx +41 -0
  905. package/src/primitives/attachment.ts +7 -0
  906. package/src/primitives/branchPicker/BranchPickerCount.tsx +33 -0
  907. package/src/primitives/branchPicker/BranchPickerNext.ts +42 -0
  908. package/src/primitives/branchPicker/BranchPickerNumber.tsx +16 -0
  909. package/src/primitives/branchPicker/BranchPickerPrevious.ts +42 -0
  910. package/src/primitives/branchPicker/BranchPickerRoot.tsx +64 -0
  911. package/src/primitives/branchPicker.ts +5 -0
  912. package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.test.tsx +116 -0
  913. package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.tsx +63 -0
  914. package/src/primitives/chainOfThought/ChainOfThoughtParts.tsx +3 -0
  915. package/src/primitives/chainOfThought/ChainOfThoughtRoot.tsx +33 -0
  916. package/src/primitives/chainOfThought.ts +14 -0
  917. package/src/primitives/composer/ComposerAddAttachment.test.tsx +101 -0
  918. package/src/primitives/composer/ComposerAddAttachment.thread-scope.test.tsx +225 -0
  919. package/src/primitives/composer/ComposerAddAttachment.ts +88 -0
  920. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +351 -0
  921. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +109 -0
  922. package/src/primitives/composer/ComposerAttachments.tsx +6 -0
  923. package/src/primitives/composer/ComposerCancel.focus.test.tsx +324 -0
  924. package/src/primitives/composer/ComposerCancel.ts +52 -0
  925. package/src/primitives/composer/ComposerCompactContext.tsx +10 -0
  926. package/src/primitives/composer/ComposerDictate.ts +37 -0
  927. package/src/primitives/composer/ComposerDictationTranscript.tsx +55 -0
  928. package/src/primitives/composer/ComposerDisabledThread.test.tsx +118 -0
  929. package/src/primitives/composer/ComposerIf.ts +4 -0
  930. package/src/primitives/composer/ComposerInput.focus-on-run-start.test.tsx +145 -0
  931. package/src/primitives/composer/ComposerInput.test.tsx +712 -0
  932. package/src/primitives/composer/ComposerInput.text-change.test.tsx +77 -0
  933. package/src/primitives/composer/ComposerInput.thread-scope.test.tsx +83 -0
  934. package/src/primitives/composer/ComposerInput.tsx +462 -0
  935. package/src/primitives/composer/ComposerInputPluginContext.tsx +80 -0
  936. package/src/primitives/composer/ComposerPreview.native.test.ts +157 -0
  937. package/src/primitives/composer/ComposerQueue.tsx +3 -0
  938. package/src/primitives/composer/ComposerQuote.tsx +143 -0
  939. package/src/primitives/composer/ComposerResume.ts +35 -0
  940. package/src/primitives/composer/ComposerRoot.test.tsx +429 -0
  941. package/src/primitives/composer/ComposerRoot.tsx +155 -0
  942. package/src/primitives/composer/ComposerSend.ts +43 -0
  943. package/src/primitives/composer/ComposerStopDictation.ts +43 -0
  944. package/src/primitives/composer/composerInputSelector.ts +4 -0
  945. package/src/primitives/composer/trigger/TriggerPopover.native.test.ts +309 -0
  946. package/src/primitives/composer/trigger/TriggerPopover.tsx +248 -0
  947. package/src/primitives/composer/trigger/TriggerPopoverAction.tsx +61 -0
  948. package/src/primitives/composer/trigger/TriggerPopoverBack.tsx +51 -0
  949. package/src/primitives/composer/trigger/TriggerPopoverBehavior.test.tsx +113 -0
  950. package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +123 -0
  951. package/src/primitives/composer/trigger/TriggerPopoverDirective.tsx +55 -0
  952. package/src/primitives/composer/trigger/TriggerPopoverExports.test.ts +61 -0
  953. package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +121 -0
  954. package/src/primitives/composer/trigger/TriggerPopoverResource.ts +146 -0
  955. package/src/primitives/composer/trigger/TriggerPopoverRootContext.test.tsx +159 -0
  956. package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +236 -0
  957. package/src/primitives/composer/trigger/detectTrigger.test.ts +197 -0
  958. package/src/primitives/composer/trigger/detectTrigger.ts +78 -0
  959. package/src/primitives/composer/trigger/index.ts +33 -0
  960. package/src/primitives/composer/trigger/matchesTriggerItemQuery.test.ts +52 -0
  961. package/src/primitives/composer/trigger/matchesTriggerItemQuery.ts +13 -0
  962. package/src/primitives/composer/trigger/triggerDetectionResource.ts +47 -0
  963. package/src/primitives/composer/trigger/triggerKeyboardResource.test.ts +233 -0
  964. package/src/primitives/composer/trigger/triggerKeyboardResource.ts +150 -0
  965. package/src/primitives/composer/trigger/triggerNavigationResource.ts +129 -0
  966. package/src/primitives/composer/trigger/triggerSelectionResource.test.ts +173 -0
  967. package/src/primitives/composer/trigger/triggerSelectionResource.ts +133 -0
  968. package/src/primitives/composer/useAuiState.composer.test.tsx +93 -0
  969. package/src/primitives/composer/useComposerCancelWithFocus.ts +51 -0
  970. package/src/primitives/composer/useComposerInputState.ts +58 -0
  971. package/src/primitives/composer/useTextareaAutosize.ts +140 -0
  972. package/src/primitives/composer.ts +88 -0
  973. package/src/primitives/dropdownMenuRenderPrimitives.test.tsx +64 -0
  974. package/src/primitives/dropdownMenuRenderPrimitives.ts +14 -0
  975. package/src/primitives/error/ErrorMessage.tsx +39 -0
  976. package/src/primitives/error/ErrorRoot.tsx +34 -0
  977. package/src/primitives/error.ts +2 -0
  978. package/src/primitives/message/MessageAttachments.tsx +6 -0
  979. package/src/primitives/message/MessageError.ts +13 -0
  980. package/src/primitives/message/MessageIf.test.tsx +83 -0
  981. package/src/primitives/message/MessageIf.ts +110 -0
  982. package/src/primitives/message/MessageParts.test.tsx +119 -0
  983. package/src/primitives/message/MessageParts.tsx +64 -0
  984. package/src/primitives/message/MessagePartsGrouped.test.tsx +682 -0
  985. package/src/primitives/message/MessagePartsGrouped.tool-interaction.test.tsx +97 -0
  986. package/src/primitives/message/MessagePartsGrouped.tsx +374 -0
  987. package/src/primitives/message/MessageRoot.anchors.test.ts +167 -0
  988. package/src/primitives/message/MessageRoot.test.tsx +272 -0
  989. package/src/primitives/message/MessageRoot.tsx +145 -0
  990. package/src/primitives/message.ts +15 -0
  991. package/src/primitives/messagePart/MessagePartImage.tsx +29 -0
  992. package/src/primitives/messagePart/MessagePartInProgress.ts +3 -0
  993. package/src/primitives/messagePart/MessagePartText.test.tsx +114 -0
  994. package/src/primitives/messagePart/MessagePartText.tsx +54 -0
  995. package/src/primitives/messagePart/useMessagePartData.test.ts +118 -0
  996. package/src/primitives/messagePart/useMessagePartData.ts +34 -0
  997. package/src/primitives/messagePart/useMessagePartFile.ts +42 -0
  998. package/src/primitives/messagePart/useMessagePartImage.ts +41 -0
  999. package/src/primitives/messagePart/useMessagePartReasoning.ts +42 -0
  1000. package/src/primitives/messagePart/useMessagePartSource.ts +43 -0
  1001. package/src/primitives/messagePart/useMessagePartText.ts +45 -0
  1002. package/src/primitives/messagePart.ts +4 -0
  1003. package/src/primitives/native-action-bindings.test.ts +181 -0
  1004. package/src/primitives/native-display-bindings.test.ts +159 -0
  1005. package/src/primitives/queueItem/QueueItemRemove.ts +36 -0
  1006. package/src/primitives/queueItem/QueueItemSteer.ts +36 -0
  1007. package/src/primitives/queueItem/QueueItemText.tsx +48 -0
  1008. package/src/primitives/queueItem.ts +3 -0
  1009. package/src/primitives/reasoning/useScrollLock.test.tsx +296 -0
  1010. package/src/primitives/reasoning/useScrollLock.ts +160 -0
  1011. package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +56 -0
  1012. package/src/primitives/selectionToolbar/SelectionToolbarRoot.test.tsx +439 -0
  1013. package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +206 -0
  1014. package/src/primitives/selectionToolbar.ts +8 -0
  1015. package/src/primitives/suggestion/SuggestionDescription.tsx +35 -0
  1016. package/src/primitives/suggestion/SuggestionTitle.tsx +43 -0
  1017. package/src/primitives/suggestion/SuggestionTrigger.ts +59 -0
  1018. package/src/primitives/suggestion.ts +3 -0
  1019. package/src/primitives/thread/ThreadEmpty.ts +19 -0
  1020. package/src/primitives/thread/ThreadIf.ts +42 -0
  1021. package/src/primitives/thread/ThreadLoadEarlier.tsx +34 -0
  1022. package/src/primitives/thread/ThreadMessages.tsx +9 -0
  1023. package/src/primitives/thread/ThreadRoot.test.tsx +419 -0
  1024. package/src/primitives/thread/ThreadRoot.tsx +119 -0
  1025. package/src/primitives/thread/ThreadRootElementContext.ts +9 -0
  1026. package/src/primitives/thread/ThreadScrollToBottom.ts +45 -0
  1027. package/src/primitives/thread/ThreadSuggestion.ts +55 -0
  1028. package/src/primitives/thread/ThreadSuggestions.tsx +7 -0
  1029. package/src/primitives/thread/ThreadViewport.thread-scope.test.tsx +58 -0
  1030. package/src/primitives/thread/ThreadViewport.tsx +219 -0
  1031. package/src/primitives/thread/ThreadViewportFooter.tsx +46 -0
  1032. package/src/primitives/thread/topAnchor/computeTopAnchorSlack.test.ts +131 -0
  1033. package/src/primitives/thread/topAnchor/computeTopAnchorSlack.ts +94 -0
  1034. package/src/primitives/thread/topAnchor/createReserveObservers.ts +50 -0
  1035. package/src/primitives/thread/topAnchor/mountTopAnchorReserve.test.ts +570 -0
  1036. package/src/primitives/thread/topAnchor/mountTopAnchorReserve.ts +221 -0
  1037. package/src/primitives/thread/topAnchor/topAnchorTurn.test.ts +115 -0
  1038. package/src/primitives/thread/topAnchor/topAnchorTurn.ts +59 -0
  1039. package/src/primitives/thread/topAnchor/topAnchorUtils.ts +58 -0
  1040. package/src/primitives/thread/topAnchor/useTopAnchorReserve.ts +23 -0
  1041. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +1887 -0
  1042. package/src/primitives/thread/useThreadViewportAutoScroll.ts +140 -0
  1043. package/src/primitives/thread.ts +17 -0
  1044. package/src/primitives/threadList/ThreadListItems.tsx +6 -0
  1045. package/src/primitives/threadList/ThreadListLoadMore.tsx +25 -0
  1046. package/src/primitives/threadList/ThreadListNew.tsx +47 -0
  1047. package/src/primitives/threadList/ThreadListRoot.tsx +29 -0
  1048. package/src/primitives/threadList.ts +5 -0
  1049. package/src/primitives/threadListFocusGroup.ts +91 -0
  1050. package/src/primitives/threadListItem/ThreadListItemArchive.ts +23 -0
  1051. package/src/primitives/threadListItem/ThreadListItemDelete.ts +23 -0
  1052. package/src/primitives/threadListItem/ThreadListItemRoot.tsx +100 -0
  1053. package/src/primitives/threadListItem/ThreadListItemTitle.test.tsx +63 -0
  1054. package/src/primitives/threadListItem/ThreadListItemTitle.tsx +3 -0
  1055. package/src/primitives/threadListItem/ThreadListItemTrigger.tsx +51 -0
  1056. package/src/primitives/threadListItem/ThreadListItemUnarchive.test.tsx +55 -0
  1057. package/src/primitives/threadListItem/ThreadListItemUnarchive.ts +25 -0
  1058. package/src/primitives/threadListItem/ThreadListKeyboardNav.test.tsx +348 -0
  1059. package/src/primitives/threadListItem.ts +6 -0
  1060. package/src/primitives/threadListItemMore/ThreadListItemMoreContent.tsx +81 -0
  1061. package/src/primitives/threadListItemMore/ThreadListItemMoreItem.tsx +22 -0
  1062. package/src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx +79 -0
  1063. package/src/primitives/threadListItemMore/ThreadListItemMoreSeparator.tsx +26 -0
  1064. package/src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx +62 -0
  1065. package/src/primitives/threadListItemMore/scope.ts +6 -0
  1066. package/src/primitives/threadListItemMore.ts +5 -0
  1067. package/src/sandbox-host/SandboxHost.test.tsx +579 -0
  1068. package/src/sandbox-host/SandboxHost.tsx +271 -0
  1069. package/src/tests/DataRenderers.test.tsx +218 -0
  1070. package/src/tests/MessageParts.loading.test.tsx +105 -0
  1071. package/src/tests/MessageParts.rendererOptions.test.tsx +125 -0
  1072. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +187 -0
  1073. package/src/tests/RemoteThreadListRuntime.deferredProvider.test.tsx +99 -0
  1074. package/src/tests/RemoteThreadListRuntime.reloadMainThread.test.tsx +113 -0
  1075. package/src/tests/augmentations.test.ts +95 -0
  1076. package/src/tests/generative-ui.primitive.test.tsx +176 -0
  1077. package/src/tests/generative-ui.test.tsx +130 -0
  1078. package/src/tests/in-memory-thread-list.test.tsx +142 -0
  1079. package/src/tests/local-runtime-queue.test.tsx +506 -0
  1080. package/src/tests/messageAction.ts +67 -0
  1081. package/src/tests/messagePartSwitchRace.test.tsx +222 -0
  1082. package/src/tests/messagePartTypeMismatch.test.tsx +153 -0
  1083. package/src/tests/remote-thread-list-test-helpers.ts +40 -0
  1084. package/src/tests/setup.ts +5 -0
  1085. package/src/tests/threadListItemIsRunning.test.tsx +215 -0
  1086. package/src/tests/threadMessageById.test.tsx +293 -0
  1087. package/src/tests/toolCallTiming.test.tsx +324 -0
  1088. package/src/tests/vue.ts +124 -0
  1089. package/src/unstable/useComposerInput.test.tsx +201 -0
  1090. package/src/unstable/useComposerInput.ts +125 -0
  1091. package/src/unstable/useComposerInputHistory.test.tsx +207 -0
  1092. package/src/unstable/useComposerInputHistory.thread-scope.test.tsx +128 -0
  1093. package/src/unstable/useComposerInputHistory.ts +163 -0
  1094. package/src/unstable/useLiveCompletionAdapter.test.tsx +760 -0
  1095. package/src/unstable/useLiveCompletionAdapter.ts +217 -0
  1096. package/src/unstable/useMentionAdapter.test.tsx +380 -0
  1097. package/src/unstable/useMentionAdapter.ts +276 -0
  1098. package/src/unstable/useMessageStallDetection.lifecycle.test.ts +120 -0
  1099. package/src/unstable/useMessageStallDetection.test.tsx +59 -0
  1100. package/src/unstable/useMessageStallDetection.ts +147 -0
  1101. package/src/unstable/useModelContextSnapshot.test.tsx +181 -0
  1102. package/src/unstable/useModelContextSnapshot.ts +85 -0
  1103. package/src/unstable/useSlashCommandAdapter.test.tsx +172 -0
  1104. package/src/unstable/useSlashCommandAdapter.ts +145 -0
  1105. package/src/unstable/webmcp/WebMcpRegistrationResource.test.ts +150 -0
  1106. package/src/unstable/webmcp/WebMcpRegistrationResource.ts +92 -0
  1107. package/src/unstable/webmcp/__tests__/webmcp.fake.tsx +177 -0
  1108. package/src/unstable/webmcp/convertTools.test.ts +605 -0
  1109. package/src/unstable/webmcp/convertTools.ts +256 -0
  1110. package/src/unstable/webmcp/useWebMcpProvider.test.tsx +543 -0
  1111. package/src/unstable/webmcp/useWebMcpProvider.ts +185 -0
  1112. package/src/unstable/webmcp/webmcp-host.test.ts +142 -0
  1113. package/src/unstable/webmcp/webmcp-host.ts +83 -0
  1114. package/src/utils/Primitive.test.tsx +551 -0
  1115. package/src/utils/Primitive.tsx +363 -0
  1116. package/src/utils/RequireAtLeastOne.ts +7 -0
  1117. package/src/utils/createActionButton.child-cancel.test.tsx +82 -0
  1118. package/src/utils/createActionButton.test.tsx +114 -0
  1119. package/src/utils/createActionButton.tsx +75 -0
  1120. package/src/utils/getSelectionMessageId.test.ts +609 -0
  1121. package/src/utils/getSelectionMessageId.ts +133 -0
  1122. package/src/utils/hooks/useManagedRef.ts +19 -0
  1123. package/src/utils/hooks/useMediaQuery.test.tsx +60 -0
  1124. package/src/utils/hooks/useMediaQuery.ts +44 -0
  1125. package/src/utils/hooks/useOnScrollToBottom.ts +23 -0
  1126. package/src/utils/hooks/useSizeHandle.ts +39 -0
  1127. package/src/utils/isCompositionKey.ts +5 -0
  1128. package/src/utils/smooth/SmoothContext.test.tsx +224 -0
  1129. package/src/utils/smooth/SmoothContext.tsx +82 -0
  1130. package/src/utils/smooth/useSmooth.test.tsx +499 -0
  1131. package/src/utils/smooth/useSmooth.ts +266 -0
  1132. package/src/utils/useIsomorphicLayoutEffect.test.tsx +25 -0
  1133. package/src/utils/useIsomorphicLayoutEffect.ts +1 -0
@@ -0,0 +1,1887 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { fireEvent, screen, waitFor } from "@testing-library/dom";
4
+ import {
5
+ actAsync as act,
6
+ act as actSync,
7
+ cleanup,
8
+ render,
9
+ } from "../../tests/vue";
10
+ import {
11
+ afterAll,
12
+ afterEach,
13
+ beforeAll,
14
+ describe,
15
+ expect,
16
+ it,
17
+ vi,
18
+ } from "vitest";
19
+ import {
20
+ defineComponent,
21
+ onMounted,
22
+ shallowRef,
23
+ type ComponentPublicInstance,
24
+ type FunctionalComponent,
25
+ } from "vue";
26
+ import { useAuiState } from "@ulearning/ai-store";
27
+ import { AssistantRuntimeProvider } from "../../context";
28
+ import {
29
+ useThreadViewport,
30
+ useThreadViewportStore,
31
+ } from "../../context/vue/ThreadViewportContext";
32
+ import * as MessagePrimitive from "../message";
33
+ import { ThreadPrimitiveMessages } from "./ThreadMessages";
34
+ import { ThreadPrimitiveRoot } from "./ThreadRoot";
35
+ import { ThreadPrimitiveScrollToBottom } from "./ThreadScrollToBottom";
36
+ import { ThreadPrimitiveViewport } from "./ThreadViewport";
37
+ import { ThreadPrimitiveViewportFooter } from "./ThreadViewportFooter";
38
+ import {
39
+ ExportedMessageRepository,
40
+ useExternalStoreRuntime,
41
+ useLocalRuntime,
42
+ type ChatModelAdapter,
43
+ type ThreadHistoryAdapter,
44
+ type ThreadMessageLike,
45
+ } from "../../index";
46
+ import { ThreadPrimitiveLoadEarlier } from "./ThreadLoadEarlier";
47
+
48
+ const adapter: ChatModelAdapter = {
49
+ async *run() {},
50
+ };
51
+
52
+ const messages: ThreadMessageLike[] = Array.from({ length: 8 }, (_, index) => ({
53
+ role: index % 2 === 0 ? "user" : "assistant",
54
+ content: [{ type: "text", text: `Message ${index + 1}` }],
55
+ }));
56
+
57
+ const getViewport = () => screen.getByTestId("viewport");
58
+
59
+ const getMaxScrollTop = (element: Element) =>
60
+ Math.max(0, element.scrollHeight - element.clientHeight);
61
+
62
+ let forceShortViewportMeasurement = false;
63
+ let viewportMeasurementOffset = 0;
64
+ const messageHeights = new Map<string, number>();
65
+ const resizeObservers = new Set<TestResizeObserver>();
66
+
67
+ const messageRows = () => [
68
+ ...document.querySelectorAll<HTMLElement>('[data-testid="thread-message"]'),
69
+ ];
70
+
71
+ const rowHeight = (row: Element) =>
72
+ messageHeights.get(row.getAttribute("data-message-id") ?? "") ?? 80;
73
+
74
+ const rowsHeight = (rows: readonly Element[]) =>
75
+ rows.reduce((height, row) => height + rowHeight(row), 0);
76
+
77
+ class TestResizeObserver {
78
+ readonly targets = new Set<Element>();
79
+ readonly callback: ResizeObserverCallback;
80
+
81
+ constructor(callback: ResizeObserverCallback) {
82
+ this.callback = callback;
83
+ resizeObservers.add(this);
84
+ }
85
+
86
+ observe(target: Element) {
87
+ this.targets.add(target);
88
+ }
89
+ unobserve(target: Element) {
90
+ this.targets.delete(target);
91
+ }
92
+ disconnect() {
93
+ resizeObservers.delete(this);
94
+ }
95
+ }
96
+
97
+ const notifyResizeObservers = () => {
98
+ for (const observer of resizeObservers) {
99
+ observer.callback([], {} as ResizeObserver);
100
+ }
101
+ };
102
+
103
+ /** Resizes only `target`, as a child growing without a DOM mutation does. */
104
+ const notifyResizeOf = (target: Element) => {
105
+ for (const observer of resizeObservers) {
106
+ if (observer.targets.has(target)) {
107
+ observer.callback([], {} as ResizeObserver);
108
+ }
109
+ }
110
+ };
111
+
112
+ const descriptors = {
113
+ scrollTop: Object.getOwnPropertyDescriptor(
114
+ HTMLElement.prototype,
115
+ "scrollTop",
116
+ ),
117
+ scrollHeight: Object.getOwnPropertyDescriptor(
118
+ HTMLElement.prototype,
119
+ "scrollHeight",
120
+ ),
121
+ clientHeight: Object.getOwnPropertyDescriptor(
122
+ HTMLElement.prototype,
123
+ "clientHeight",
124
+ ),
125
+ scrollTo: Object.getOwnPropertyDescriptor(HTMLElement.prototype, "scrollTo"),
126
+ };
127
+
128
+ const scrollTopByElement = new WeakMap<Element, number>();
129
+
130
+ beforeAll(() => {
131
+ vi.stubGlobal("ResizeObserver", TestResizeObserver);
132
+ vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) =>
133
+ window.setTimeout(() => callback(performance.now()), 0),
134
+ );
135
+ vi.stubGlobal("cancelAnimationFrame", (id: number) =>
136
+ window.clearTimeout(id),
137
+ );
138
+
139
+ Object.defineProperty(HTMLElement.prototype, "scrollTop", {
140
+ configurable: true,
141
+ get() {
142
+ return scrollTopByElement.get(this) ?? 0;
143
+ },
144
+ set(value: number) {
145
+ scrollTopByElement.set(this, value);
146
+ },
147
+ });
148
+
149
+ Object.defineProperty(HTMLElement.prototype, "clientHeight", {
150
+ configurable: true,
151
+ get() {
152
+ return this.getAttribute("data-testid") === "viewport" ? 100 : 0;
153
+ },
154
+ });
155
+
156
+ Object.defineProperty(HTMLElement.prototype, "scrollHeight", {
157
+ configurable: true,
158
+ get() {
159
+ if (this.getAttribute("data-testid") !== "viewport") return 0;
160
+ if (forceShortViewportMeasurement) return this.clientHeight;
161
+ return rowsHeight(messageRows()) + viewportMeasurementOffset;
162
+ },
163
+ });
164
+
165
+ Object.defineProperty(HTMLElement.prototype, "getBoundingClientRect", {
166
+ configurable: true,
167
+ value(this: HTMLElement) {
168
+ const rows = messageRows();
169
+ const index = rows.indexOf(this);
170
+ const viewport = this.closest<HTMLElement>('[data-testid="viewport"]');
171
+ const top =
172
+ index === -1
173
+ ? 0
174
+ : rowsHeight(rows.slice(0, index)) - (viewport?.scrollTop ?? 0);
175
+ const height = index === -1 ? 0 : rowHeight(this);
176
+ return {
177
+ x: 0,
178
+ y: top,
179
+ top,
180
+ bottom: top + height,
181
+ left: 0,
182
+ right: 0,
183
+ width: 0,
184
+ height,
185
+ toJSON: () => ({}),
186
+ } satisfies DOMRect;
187
+ },
188
+ });
189
+
190
+ Object.defineProperty(HTMLElement.prototype, "scrollTo", {
191
+ configurable: true,
192
+ value({ top = 0 }: ScrollToOptions) {
193
+ this.scrollTop = Math.min(Number(top), getMaxScrollTop(this));
194
+ this.dispatchEvent(new Event("scroll"));
195
+ },
196
+ });
197
+ });
198
+
199
+ afterEach(() => {
200
+ forceShortViewportMeasurement = false;
201
+ viewportMeasurementOffset = 0;
202
+ messageHeights.clear();
203
+ resizeObservers.clear();
204
+ cleanup();
205
+ });
206
+
207
+ afterAll(() => {
208
+ vi.unstubAllGlobals();
209
+
210
+ for (const [key, descriptor] of Object.entries(descriptors)) {
211
+ if (descriptor) {
212
+ Object.defineProperty(HTMLElement.prototype, key, descriptor);
213
+ }
214
+ }
215
+ });
216
+
217
+ const Message: FunctionalComponent = () => (
218
+ <MessagePrimitive.Root data-testid="thread-message">
219
+ <MessagePrimitive.Content />
220
+ </MessagePrimitive.Root>
221
+ );
222
+
223
+ const AtBottom = defineComponent(() => {
224
+ const isAtBottom = useThreadViewport((s) => s.isAtBottom);
225
+ return () => (
226
+ <output data-testid="is-at-bottom">{String(isAtBottom.value)}</output>
227
+ );
228
+ });
229
+
230
+ const RequestSmoothScrollToBottom = defineComponent(() => {
231
+ const threadViewportStore = useThreadViewportStore();
232
+
233
+ onMounted(() => {
234
+ threadViewportStore.getState().scrollToBottom({ behavior: "smooth" });
235
+ });
236
+
237
+ return () => null;
238
+ });
239
+
240
+ const Thread = ({
241
+ autoScroll,
242
+ scrollToBottomOnInitialize,
243
+ scrollToBottomOnThreadSwitch,
244
+ }: {
245
+ autoScroll?: boolean | undefined;
246
+ scrollToBottomOnInitialize?: boolean | undefined;
247
+ scrollToBottomOnThreadSwitch?: boolean | undefined;
248
+ }) => (
249
+ <ThreadPrimitiveRoot>
250
+ <ThreadPrimitiveViewport
251
+ autoScroll={autoScroll}
252
+ data-testid="viewport"
253
+ turnAnchor="top"
254
+ scrollToBottomOnInitialize={scrollToBottomOnInitialize}
255
+ scrollToBottomOnThreadSwitch={scrollToBottomOnThreadSwitch}
256
+ >
257
+ <ThreadPrimitiveMessages components={{ Message }} />
258
+ <AtBottom />
259
+ {/* The canonical Thread renders its composer inside the viewport, so
260
+ composer keystrokes bubble to the viewport's keydown listener. */}
261
+ <textarea data-testid="composer" />
262
+ {/* An input that activates on a key, and a `contenteditable="false"`
263
+ element in message content, both act on content rather than accept
264
+ text. Neither is a text-entry surface. */}
265
+ <input type="checkbox" data-testid="checkbox" />
266
+ <span contenteditable={false} data-testid="readonly-island" tabindex={0}>
267
+ tool call
268
+ </span>
269
+ </ThreadPrimitiveViewport>
270
+ </ThreadPrimitiveRoot>
271
+ );
272
+
273
+ const BottomAnchorThread = () => (
274
+ <ThreadPrimitiveRoot>
275
+ <ThreadPrimitiveViewport data-testid="viewport">
276
+ <ThreadPrimitiveMessages components={{ Message }} />
277
+ <AtBottom />
278
+ </ThreadPrimitiveViewport>
279
+ </ThreadPrimitiveRoot>
280
+ );
281
+
282
+ const RunState = defineComponent(() => {
283
+ const isRunning = useAuiState((s) => s.thread.isRunning);
284
+ return () => (
285
+ <output data-testid="run-state">{String(isRunning.value)}</output>
286
+ );
287
+ });
288
+
289
+ const SyncRuntimeProvider = defineComponent((_, { slots }) => {
290
+ const runtime = useLocalRuntime(adapter, { initialMessages: messages });
291
+
292
+ return () => (
293
+ <AssistantRuntimeProvider runtime={runtime}>
294
+ {slots.default?.()}
295
+ </AssistantRuntimeProvider>
296
+ );
297
+ });
298
+
299
+ const AsyncRuntimeProvider = defineComponent((_, { slots }) => {
300
+ const history: ThreadHistoryAdapter = {
301
+ async load() {
302
+ await Promise.resolve();
303
+ return ExportedMessageRepository.fromArray(messages);
304
+ },
305
+ async append() {},
306
+ };
307
+ const runtime = useLocalRuntime(adapter, { adapters: { history } });
308
+
309
+ return () => (
310
+ <AssistantRuntimeProvider runtime={runtime}>
311
+ {slots.default?.()}
312
+ </AssistantRuntimeProvider>
313
+ );
314
+ });
315
+
316
+ const DelayedThread = defineComponent(
317
+ (props: {
318
+ autoScroll?: boolean | undefined;
319
+ scrollToBottomOnInitialize?: boolean | undefined;
320
+ }) => {
321
+ const showThread = shallowRef(false);
322
+
323
+ onMounted(() => {
324
+ showThread.value = true;
325
+ });
326
+
327
+ return () =>
328
+ !showThread.value ? null : (
329
+ <Thread
330
+ autoScroll={props.autoScroll}
331
+ scrollToBottomOnInitialize={props.scrollToBottomOnInitialize}
332
+ />
333
+ );
334
+ },
335
+ { props: ["autoScroll", "scrollToBottomOnInitialize"] },
336
+ );
337
+
338
+ const measureFooter = (instance: Element | ComponentPublicInstance | null) => {
339
+ const element = instance instanceof Element ? instance : instance?.$el;
340
+ if (!(element instanceof HTMLElement)) return;
341
+ Object.defineProperty(element, "offsetHeight", {
342
+ configurable: true,
343
+ get: () => Number.parseFloat(element.style.height),
344
+ });
345
+ };
346
+
347
+ const FooterInsetState = defineComponent(() => {
348
+ const inset = useThreadViewport((s) => s.height.inset);
349
+ return () => <output data-testid="footer-inset">{inset.value}</output>;
350
+ });
351
+
352
+ const FooterThread = ({
353
+ footerHeight,
354
+ autoScroll = false,
355
+ }: {
356
+ footerHeight: number | null;
357
+ autoScroll?: boolean;
358
+ }) => (
359
+ <SyncRuntimeProvider>
360
+ <ThreadPrimitiveViewport
361
+ data-testid="viewport"
362
+ autoScroll={autoScroll}
363
+ scrollToBottomOnInitialize={false}
364
+ >
365
+ {messages.map((_, index) => (
366
+ <div key={index} data-testid="thread-message" />
367
+ ))}
368
+ {footerHeight !== null && (
369
+ <ThreadPrimitiveViewportFooter
370
+ ref={measureFooter}
371
+ data-testid="footer"
372
+ style={{ height: `${footerHeight}px` }}
373
+ />
374
+ )}
375
+ <AtBottom />
376
+ <FooterInsetState />
377
+ </ThreadPrimitiveViewport>
378
+ </SyncRuntimeProvider>
379
+ );
380
+
381
+ describe("viewport footer insets", () => {
382
+ it("counts a footer registered before viewport attachment toward at-bottom", async () => {
383
+ render(<FooterThread footerHeight={40} />);
384
+
385
+ await act(() => {
386
+ getViewport().scrollTop = 500;
387
+ fireEvent.scroll(getViewport());
388
+ });
389
+
390
+ expect(screen.getByTestId("footer-inset").textContent).toBe("40");
391
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
392
+ });
393
+
394
+ it("follows a pinned viewport when its footer grows without a content resize", async () => {
395
+ const view = render(<FooterThread footerHeight={40} autoScroll />);
396
+ await act(notifyResizeObservers);
397
+
398
+ await act(() => {
399
+ getViewport().scrollTop = 500;
400
+ fireEvent.scroll(getViewport());
401
+ });
402
+
403
+ await view.rerender(<FooterThread footerHeight={80} autoScroll />);
404
+ await act(() => notifyResizeOf(screen.getByTestId("footer")));
405
+
406
+ expect(getViewport().scrollTop).toBe(540);
407
+ expect(screen.getByTestId("footer-inset").textContent).toBe("80");
408
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
409
+ });
410
+
411
+ it("unregisters an unmounted footer and recomputes at-bottom", async () => {
412
+ const view = render(<FooterThread footerHeight={40} />);
413
+
414
+ await act(() => {
415
+ getViewport().scrollTop = 500;
416
+ fireEvent.scroll(getViewport());
417
+ });
418
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
419
+
420
+ await view.rerender(<FooterThread footerHeight={null} />);
421
+
422
+ expect(screen.getByTestId("footer-inset").textContent).toBe("0");
423
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
424
+ expect(getViewport().scrollTop).toBe(500);
425
+ });
426
+ });
427
+
428
+ describe("useThreadViewportAutoScroll", () => {
429
+ it("keeps viewport listeners and observers across rerenders", async () => {
430
+ const view = render(
431
+ <SyncRuntimeProvider>
432
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
433
+ </SyncRuntimeProvider>,
434
+ );
435
+ await waitFor(() => expect(messageRows()).toHaveLength(messages.length));
436
+ await act(() => {});
437
+ const observe = vi.spyOn(TestResizeObserver.prototype, "observe");
438
+ const disconnect = vi.spyOn(TestResizeObserver.prototype, "disconnect");
439
+ const observeMutations = vi.spyOn(MutationObserver.prototype, "observe");
440
+ const disconnectMutations = vi.spyOn(
441
+ MutationObserver.prototype,
442
+ "disconnect",
443
+ );
444
+ const viewport = getViewport();
445
+ const addListenerSpy = vi.spyOn(viewport, "addEventListener");
446
+ const removeListenerSpy = vi.spyOn(viewport, "removeEventListener");
447
+ try {
448
+ await view.rerender(
449
+ <SyncRuntimeProvider>
450
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
451
+ </SyncRuntimeProvider>,
452
+ );
453
+
454
+ expect(observe).not.toHaveBeenCalled();
455
+ expect(disconnect).not.toHaveBeenCalled();
456
+ expect(observeMutations).not.toHaveBeenCalled();
457
+ expect(disconnectMutations).not.toHaveBeenCalled();
458
+ expect(addListenerSpy).not.toHaveBeenCalled();
459
+ expect(removeListenerSpy).not.toHaveBeenCalled();
460
+
461
+ view.unmount();
462
+ expect(disconnect).toHaveBeenCalled();
463
+ expect(disconnectMutations).toHaveBeenCalled();
464
+ expect(removeListenerSpy).toHaveBeenCalled();
465
+ } finally {
466
+ observe.mockRestore();
467
+ disconnect.mockRestore();
468
+ observeMutations.mockRestore();
469
+ disconnectMutations.mockRestore();
470
+ addListenerSpy.mockRestore();
471
+ removeListenerSpy.mockRestore();
472
+ }
473
+ });
474
+
475
+ it("preserves smooth scrolling from a control outside the viewport", async () => {
476
+ render(
477
+ <SyncRuntimeProvider>
478
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
479
+ <ThreadPrimitiveScrollToBottom behavior="smooth">
480
+ Scroll to bottom
481
+ </ThreadPrimitiveScrollToBottom>
482
+ </SyncRuntimeProvider>,
483
+ );
484
+
485
+ const viewport = getViewport();
486
+ await act(() => {
487
+ viewport.dispatchEvent(new Event("scroll"));
488
+ });
489
+ const button = screen.getByRole("button", { name: "Scroll to bottom" });
490
+ await waitFor(() => expect(button.hasAttribute("disabled")).toBe(false));
491
+
492
+ const scrollToSpy = vi.spyOn(viewport, "scrollTo");
493
+ try {
494
+ fireEvent.click(button);
495
+ expect(scrollToSpy).toHaveBeenCalledWith({
496
+ top: viewport.scrollHeight,
497
+ behavior: "smooth",
498
+ });
499
+ } finally {
500
+ scrollToSpy.mockRestore();
501
+ }
502
+ });
503
+
504
+ it("updates isAtBottom when a wheel interrupts a smooth scroll short of the bottom", async () => {
505
+ const view = render(
506
+ <SyncRuntimeProvider>
507
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
508
+ <ThreadPrimitiveScrollToBottom behavior="smooth">
509
+ Scroll to bottom
510
+ </ThreadPrimitiveScrollToBottom>
511
+ </SyncRuntimeProvider>,
512
+ );
513
+
514
+ const viewport = getViewport();
515
+ await waitFor(() => {
516
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
517
+ messages.length,
518
+ );
519
+ });
520
+
521
+ await act(() => {
522
+ viewport.scrollTop = getMaxScrollTop(viewport);
523
+ viewport.dispatchEvent(new Event("scroll"));
524
+ });
525
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
526
+ viewportMeasurementOffset += 200;
527
+
528
+ const scrollToSpy = vi
529
+ .spyOn(viewport, "scrollTo")
530
+ .mockImplementation(() => {});
531
+ try {
532
+ await view.rerender(
533
+ <SyncRuntimeProvider>
534
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
535
+ <ThreadPrimitiveScrollToBottom behavior="smooth">
536
+ Scroll to bottom
537
+ </ThreadPrimitiveScrollToBottom>
538
+ <RequestSmoothScrollToBottom />
539
+ </SyncRuntimeProvider>,
540
+ );
541
+
542
+ await waitFor(() => {
543
+ expect(scrollToSpy).toHaveBeenCalledWith({
544
+ top: viewport.scrollHeight,
545
+ behavior: "smooth",
546
+ });
547
+ });
548
+
549
+ await act(() => {
550
+ viewport.scrollTop += 100;
551
+ viewport.dispatchEvent(new Event("scroll"));
552
+ });
553
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
554
+ expect(
555
+ (
556
+ screen.getByRole("button", {
557
+ name: "Scroll to bottom",
558
+ }) as HTMLButtonElement
559
+ ).disabled,
560
+ ).toBe(true);
561
+
562
+ await act(() => {
563
+ viewport.dispatchEvent(new WheelEvent("wheel"));
564
+ });
565
+
566
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
567
+ expect(
568
+ (
569
+ screen.getByRole("button", {
570
+ name: "Scroll to bottom",
571
+ }) as HTMLButtonElement
572
+ ).disabled,
573
+ ).toBe(false);
574
+ } finally {
575
+ scrollToSpy.mockRestore();
576
+ }
577
+ });
578
+
579
+ it("keeps a coalesced user scroll-up after a programmatic bottom scroll", async () => {
580
+ render(
581
+ <SyncRuntimeProvider>
582
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
583
+ <ThreadPrimitiveScrollToBottom behavior="instant">
584
+ Scroll to bottom
585
+ </ThreadPrimitiveScrollToBottom>
586
+ </SyncRuntimeProvider>,
587
+ );
588
+
589
+ const viewport = getViewport();
590
+ await waitFor(() => {
591
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
592
+ messages.length,
593
+ );
594
+ });
595
+
596
+ await act(() => {
597
+ viewport.scrollTop = 0;
598
+ viewport.dispatchEvent(new Event("scroll"));
599
+ });
600
+ await waitFor(() => {
601
+ expect(
602
+ screen
603
+ .getByRole("button", { name: "Scroll to bottom" })
604
+ .hasAttribute("disabled"),
605
+ ).toBe(false);
606
+ });
607
+
608
+ const scrollToDescriptor = Object.getOwnPropertyDescriptor(
609
+ viewport,
610
+ "scrollTo",
611
+ );
612
+ Object.defineProperty(viewport, "scrollTo", {
613
+ configurable: true,
614
+ value: ({ top = 0 }: ScrollToOptions) => {
615
+ viewport.scrollTop = Math.min(Number(top), getMaxScrollTop(viewport));
616
+ },
617
+ });
618
+ try {
619
+ fireEvent.click(screen.getByRole("button", { name: "Scroll to bottom" }));
620
+
621
+ await act(() => {
622
+ viewport.scrollTop -= 80;
623
+ viewport.dispatchEvent(new Event("scroll"));
624
+ });
625
+
626
+ const scrollTopAfterLeave = viewport.scrollTop;
627
+ viewportMeasurementOffset += 200;
628
+ await act(notifyResizeObservers);
629
+
630
+ expect(viewport.scrollTop).toBe(scrollTopAfterLeave);
631
+ expect(viewport.scrollTop).toBeLessThan(getMaxScrollTop(viewport));
632
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
633
+ } finally {
634
+ if (scrollToDescriptor) {
635
+ Object.defineProperty(viewport, "scrollTo", scrollToDescriptor);
636
+ } else {
637
+ Reflect.deleteProperty(viewport, "scrollTo");
638
+ }
639
+ }
640
+ });
641
+
642
+ it("scrolls sync initialMessages to the bottom when the viewport mounts after initialization", async () => {
643
+ render(
644
+ <SyncRuntimeProvider>
645
+ <DelayedThread />
646
+ </SyncRuntimeProvider>,
647
+ );
648
+
649
+ await waitFor(() => {
650
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
651
+ messages.length,
652
+ );
653
+ expect(getViewport().scrollTop).toBe(getMaxScrollTop(getViewport()));
654
+ });
655
+ });
656
+
657
+ it("keeps async history initialization scroll pending until imported messages are measurable", async () => {
658
+ forceShortViewportMeasurement = true;
659
+
660
+ render(
661
+ <AsyncRuntimeProvider>
662
+ <Thread />
663
+ </AsyncRuntimeProvider>,
664
+ );
665
+
666
+ await waitFor(() => {
667
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
668
+ messages.length,
669
+ );
670
+ });
671
+
672
+ await new Promise((resolve) => setTimeout(resolve, 0));
673
+ expect(getViewport().scrollTop).toBe(0);
674
+
675
+ forceShortViewportMeasurement = false;
676
+ notifyResizeObservers();
677
+
678
+ await waitFor(() => {
679
+ expect(getViewport().scrollTop).toBe(getMaxScrollTop(getViewport()));
680
+ });
681
+ });
682
+
683
+ it("preserves run-start's auto behavior on the first message of an empty thread", async () => {
684
+ const scrollToSpy = vi.spyOn(HTMLElement.prototype, "scrollTo");
685
+
686
+ let runtime: ReturnType<typeof useLocalRuntime> | null = null;
687
+ const Harness = defineComponent(() => {
688
+ const mountedRuntime = useLocalRuntime(adapter);
689
+ runtime = mountedRuntime;
690
+ return () => (
691
+ <AssistantRuntimeProvider runtime={mountedRuntime}>
692
+ <BottomAnchorThread />
693
+ </AssistantRuntimeProvider>
694
+ );
695
+ });
696
+
697
+ render(<Harness />);
698
+
699
+ expect(screen.queryAllByTestId("thread-message")).toHaveLength(0);
700
+
701
+ await act(async () => {
702
+ runtime!.thread.append({
703
+ role: "user",
704
+ content: [{ type: "text", text: "hello" }],
705
+ });
706
+ });
707
+
708
+ await waitFor(() => {
709
+ expect(scrollToSpy).toHaveBeenCalled();
710
+ });
711
+
712
+ const behaviors = scrollToSpy.mock.calls.map(
713
+ (call) => (call[0] as ScrollToOptions).behavior,
714
+ );
715
+ expect(behaviors[0]).toBe("auto");
716
+ expect(behaviors).not.toContain("instant");
717
+
718
+ scrollToSpy.mockRestore();
719
+ });
720
+
721
+ it("keeps following after a content-growth burst undershoots the bottom", async () => {
722
+ render(
723
+ <SyncRuntimeProvider>
724
+ <BottomAnchorThread />
725
+ </SyncRuntimeProvider>,
726
+ );
727
+
728
+ const viewport = getViewport();
729
+ await waitFor(() => {
730
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
731
+ messages.length,
732
+ );
733
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
734
+ });
735
+
736
+ const scrollTopBeforeBurst = viewport.scrollTop;
737
+ viewportMeasurementOffset += 164;
738
+ actSync(() => {
739
+ viewport.scrollTop = scrollTopBeforeBurst + 106;
740
+ viewport.dispatchEvent(new Event("scroll"));
741
+ viewport.dispatchEvent(new Event("scroll"));
742
+ });
743
+ expect(viewport.scrollTop).toBeLessThan(getMaxScrollTop(viewport));
744
+
745
+ viewportMeasurementOffset += 200;
746
+ await act(notifyResizeObservers);
747
+
748
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
749
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
750
+ });
751
+
752
+ it("follows a message that grows without a DOM mutation", async () => {
753
+ render(
754
+ <SyncRuntimeProvider>
755
+ <BottomAnchorThread />
756
+ </SyncRuntimeProvider>,
757
+ );
758
+
759
+ const viewport = getViewport();
760
+ await waitFor(() => {
761
+ expect(messageRows()).toHaveLength(messages.length);
762
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
763
+ });
764
+
765
+ const lastRow = messageRows().at(-1)!;
766
+ messageHeights.set(lastRow.getAttribute("data-message-id")!, 280);
767
+ await act(() => notifyResizeOf(lastRow));
768
+
769
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
770
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
771
+ });
772
+
773
+ it("reports leaving the bottom when a message grows without a DOM mutation", async () => {
774
+ render(
775
+ <SyncRuntimeProvider>
776
+ <Thread />
777
+ </SyncRuntimeProvider>,
778
+ );
779
+
780
+ const viewport = getViewport();
781
+ await waitFor(() => {
782
+ expect(messageRows()).toHaveLength(messages.length);
783
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
784
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
785
+ });
786
+
787
+ const lastRow = messageRows().at(-1)!;
788
+ messageHeights.set(lastRow.getAttribute("data-message-id")!, 280);
789
+ await act(() => notifyResizeOf(lastRow));
790
+
791
+ expect(viewport.scrollTop).toBeLessThan(getMaxScrollTop(viewport));
792
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
793
+ });
794
+
795
+ it("keeps following after a pointerdown that does not scroll the viewport", async () => {
796
+ render(
797
+ <SyncRuntimeProvider>
798
+ <BottomAnchorThread />
799
+ </SyncRuntimeProvider>,
800
+ );
801
+
802
+ const viewport = getViewport();
803
+ await waitFor(() => {
804
+ expect(messageRows()).toHaveLength(messages.length);
805
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
806
+ });
807
+
808
+ await act(() => {
809
+ viewport.dispatchEvent(new Event("pointerdown"));
810
+ });
811
+ viewportMeasurementOffset += 200;
812
+ await act(notifyResizeObservers);
813
+
814
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
815
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("true");
816
+ });
817
+
818
+ describe("disclosures in a message", () => {
819
+ const renderPinned = async () => {
820
+ render(
821
+ <SyncRuntimeProvider>
822
+ <BottomAnchorThread />
823
+ </SyncRuntimeProvider>,
824
+ );
825
+ const viewport = getViewport();
826
+ await waitFor(() => {
827
+ expect(messageRows()).toHaveLength(messages.length);
828
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
829
+ });
830
+ return viewport;
831
+ };
832
+ const growContent = async () => {
833
+ viewportMeasurementOffset += 200;
834
+ await act(notifyResizeObservers);
835
+ };
836
+ const lastMessage = () => screen.getAllByTestId("thread-message").at(-1)!;
837
+ const addTrigger = (
838
+ attributes: Record<string, string>,
839
+ parent: Element = lastMessage(),
840
+ ) => {
841
+ const trigger = document.createElement("button");
842
+ for (const [name, value] of Object.entries(attributes))
843
+ trigger.setAttribute(name, value);
844
+ parent.append(trigger);
845
+ return trigger;
846
+ };
847
+ const click = async (target: Element) => {
848
+ await act(() => {
849
+ target.dispatchEvent(new MouseEvent("click", { bubbles: true }));
850
+ });
851
+ };
852
+
853
+ it("stops following after a click expands a collapsed trigger", async () => {
854
+ const viewport = await renderPinned();
855
+ const scrollTop = viewport.scrollTop;
856
+
857
+ await click(addTrigger({ "aria-expanded": "false" }));
858
+ await growContent();
859
+
860
+ expect(viewport.scrollTop).toBe(scrollTop);
861
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
862
+ });
863
+
864
+ it("stops following after opening a closed details summary", async () => {
865
+ const viewport = await renderPinned();
866
+ const scrollTop = viewport.scrollTop;
867
+ const details = document.createElement("details");
868
+ const summary = document.createElement("summary");
869
+ details.append(summary);
870
+ lastMessage().append(details);
871
+
872
+ await click(summary);
873
+ await growContent();
874
+
875
+ expect(viewport.scrollTop).toBe(scrollTop);
876
+ });
877
+
878
+ it("keeps the pause through a resize while the thread still fits", async () => {
879
+ forceShortViewportMeasurement = true;
880
+ const viewport = await renderPinned();
881
+
882
+ await click(addTrigger({ "aria-expanded": "false" }));
883
+ await act(notifyResizeObservers);
884
+ forceShortViewportMeasurement = false;
885
+ await growContent();
886
+
887
+ expect(viewport.scrollTop).toBe(0);
888
+ expect(getMaxScrollTop(viewport)).toBeGreaterThan(0);
889
+ });
890
+
891
+ it("follows again after the reader scrolls back to the bottom", async () => {
892
+ const viewport = await renderPinned();
893
+
894
+ await click(addTrigger({ "aria-expanded": "false" }));
895
+ await growContent();
896
+ await act(() => {
897
+ viewport.dispatchEvent(new WheelEvent("wheel"));
898
+ viewport.scrollTop = getMaxScrollTop(viewport);
899
+ viewport.dispatchEvent(new Event("scroll"));
900
+ });
901
+ await growContent();
902
+
903
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
904
+ });
905
+
906
+ it.each([
907
+ { label: "an expanded trigger", attributes: { "aria-expanded": "true" } },
908
+ {
909
+ label: "a dialog trigger",
910
+ attributes: { "aria-expanded": "false", "aria-haspopup": "dialog" },
911
+ },
912
+ {
913
+ label: "a menu trigger",
914
+ attributes: { "aria-expanded": "false", "aria-haspopup": "menu" },
915
+ },
916
+ {
917
+ label: "a combobox",
918
+ attributes: { "aria-expanded": "false", role: "combobox" },
919
+ },
920
+ { label: "a plain control", attributes: {} },
921
+ ])("keeps following after clicking $label", async ({ attributes }) => {
922
+ const viewport = await renderPinned();
923
+
924
+ await click(addTrigger(attributes));
925
+ await growContent();
926
+
927
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
928
+ });
929
+
930
+ it("keeps following after a press on a collapsed trigger that never clicks", async () => {
931
+ const viewport = await renderPinned();
932
+
933
+ await act(() => {
934
+ addTrigger({ "aria-expanded": "false" }).dispatchEvent(
935
+ new Event("pointerdown", { bubbles: true }),
936
+ );
937
+ });
938
+ await growContent();
939
+
940
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
941
+ });
942
+
943
+ it("keeps following after expanding a trigger outside the messages", async () => {
944
+ const viewport = await renderPinned();
945
+
946
+ await click(addTrigger({ "aria-expanded": "false" }, viewport));
947
+ await growContent();
948
+
949
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
950
+ });
951
+ it.each([
952
+ {
953
+ label: "flips its state natively",
954
+ listen: (trigger: HTMLElement) =>
955
+ trigger.addEventListener("click", () =>
956
+ trigger.setAttribute("aria-expanded", "true"),
957
+ ),
958
+ },
959
+ {
960
+ label: "stops the event",
961
+ listen: (trigger: HTMLElement) =>
962
+ trigger.addEventListener("click", (event) => event.stopPropagation()),
963
+ },
964
+ ])("stops following when the trigger $label", async ({ listen }) => {
965
+ const viewport = await renderPinned();
966
+ const scrollTop = viewport.scrollTop;
967
+ const trigger = addTrigger({ "aria-expanded": "false" });
968
+ listen(trigger);
969
+
970
+ await click(trigger);
971
+ await growContent();
972
+
973
+ expect(viewport.scrollTop).toBe(scrollTop);
974
+ });
975
+
976
+ it("keeps the pause when a scroll gesture stops short of the bottom and the thread then fits", async () => {
977
+ const viewport = await renderPinned();
978
+
979
+ await click(addTrigger({ "aria-expanded": "false" }));
980
+ await growContent();
981
+ await act(() => {
982
+ viewport.dispatchEvent(new WheelEvent("wheel"));
983
+ viewport.scrollTop = getMaxScrollTop(viewport) - 50;
984
+ viewport.dispatchEvent(new Event("scroll"));
985
+ });
986
+ forceShortViewportMeasurement = true;
987
+ await act(notifyResizeObservers);
988
+ forceShortViewportMeasurement = false;
989
+ const scrollTop = viewport.scrollTop;
990
+ await growContent();
991
+
992
+ expect(viewport.scrollTop).toBe(scrollTop);
993
+ });
994
+
995
+ it("treats aria-haspopup=false as a disclosure", async () => {
996
+ const viewport = await renderPinned();
997
+ const scrollTop = viewport.scrollTop;
998
+
999
+ await click(
1000
+ addTrigger({ "aria-expanded": "false", "aria-haspopup": "false" }),
1001
+ );
1002
+ await growContent();
1003
+
1004
+ expect(viewport.scrollTop).toBe(scrollTop);
1005
+ });
1006
+
1007
+ it("follows again after the reader pages back to the bottom with Space", async () => {
1008
+ const viewport = await renderPinned();
1009
+
1010
+ await click(addTrigger({ "aria-expanded": "false" }));
1011
+ await growContent();
1012
+ await act(() => {
1013
+ viewport.dispatchEvent(
1014
+ new KeyboardEvent("keydown", { key: " ", bubbles: true }),
1015
+ );
1016
+ viewport.scrollTop = getMaxScrollTop(viewport);
1017
+ viewport.dispatchEvent(new Event("scroll"));
1018
+ });
1019
+ await growContent();
1020
+
1021
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1022
+ });
1023
+
1024
+ it("keeps following after a non-activating key on a collapsed trigger", async () => {
1025
+ const viewport = await renderPinned();
1026
+
1027
+ await act(() => {
1028
+ addTrigger({ "aria-expanded": "false" }).dispatchEvent(
1029
+ new KeyboardEvent("keydown", { key: "Tab", bubbles: true }),
1030
+ );
1031
+ });
1032
+ await growContent();
1033
+
1034
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1035
+ });
1036
+
1037
+ const renderWithoutRunStartScroll = async () => {
1038
+ let runtime: ReturnType<typeof useLocalRuntime> | null = null;
1039
+ const Harness = defineComponent(() => {
1040
+ const mountedRuntime = useLocalRuntime(adapter, {
1041
+ initialMessages: messages,
1042
+ });
1043
+ runtime = mountedRuntime;
1044
+ return () => (
1045
+ <AssistantRuntimeProvider runtime={mountedRuntime}>
1046
+ <ThreadPrimitiveRoot>
1047
+ <ThreadPrimitiveViewport
1048
+ data-testid="viewport"
1049
+ scrollToBottomOnRunStart={false}
1050
+ >
1051
+ <ThreadPrimitiveMessages components={{ Message }} />
1052
+ <AtBottom />
1053
+ </ThreadPrimitiveViewport>
1054
+ </ThreadPrimitiveRoot>
1055
+ </AssistantRuntimeProvider>
1056
+ );
1057
+ });
1058
+ render(<Harness />);
1059
+ const viewport = getViewport();
1060
+ await waitFor(() => {
1061
+ expect(messageRows()).toHaveLength(messages.length);
1062
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1063
+ });
1064
+ const startRun = async () =>
1065
+ await act(async () => {
1066
+ runtime!.thread.append({
1067
+ role: "user",
1068
+ content: [{ type: "text", text: "next" }],
1069
+ });
1070
+ });
1071
+ return { viewport, startRun };
1072
+ };
1073
+
1074
+ it("clears the pause when a run starts, even when run start does not scroll", async () => {
1075
+ const { viewport, startRun } = await renderWithoutRunStartScroll();
1076
+
1077
+ await click(addTrigger({ "aria-expanded": "false" }));
1078
+ await startRun();
1079
+ await growContent();
1080
+
1081
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1082
+ });
1083
+
1084
+ it("follows again after a run starts even when the expansion moved the reader off the bottom", async () => {
1085
+ const { viewport, startRun } = await renderWithoutRunStartScroll();
1086
+
1087
+ await click(addTrigger({ "aria-expanded": "false" }));
1088
+ await growContent();
1089
+ expect(viewport.scrollTop).toBeLessThan(getMaxScrollTop(viewport));
1090
+ await startRun();
1091
+ await growContent();
1092
+
1093
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1094
+ });
1095
+
1096
+ it("keeps the reader's place after a run starts when they scrolled up during the pause", async () => {
1097
+ const { viewport, startRun } = await renderWithoutRunStartScroll();
1098
+
1099
+ await click(addTrigger({ "aria-expanded": "false" }));
1100
+ await act(() => {
1101
+ viewport.dispatchEvent(new WheelEvent("wheel"));
1102
+ viewport.scrollTop = getMaxScrollTop(viewport) - 50;
1103
+ viewport.dispatchEvent(new Event("scroll"));
1104
+ });
1105
+ const scrollTop = viewport.scrollTop;
1106
+ await startRun();
1107
+ await growContent();
1108
+
1109
+ expect(viewport.scrollTop).toBe(scrollTop);
1110
+ });
1111
+
1112
+ it("clears the pause on a thread switch, even when the switch does not scroll", async () => {
1113
+ const SwitchingThread = defineComponent(() => {
1114
+ const thread = shallowRef({ id: "a", messages });
1115
+ const runtime = useExternalStoreRuntime<ThreadMessageLike>(() => ({
1116
+ messages: thread.value.messages,
1117
+ convertMessage: (message) => message,
1118
+ onNew: async () => {},
1119
+ adapters: {
1120
+ threadList: {
1121
+ threadId: thread.value.id,
1122
+ threads: [
1123
+ { id: "a", status: "regular" },
1124
+ { id: "b", status: "regular" },
1125
+ ],
1126
+ },
1127
+ },
1128
+ }));
1129
+ return () => (
1130
+ <AssistantRuntimeProvider runtime={runtime}>
1131
+ <Thread autoScroll scrollToBottomOnThreadSwitch={false} />
1132
+ <button
1133
+ type="button"
1134
+ data-testid="switch"
1135
+ onClick={() =>
1136
+ (thread.value = {
1137
+ id: "b",
1138
+ messages: messages.map((message, index) => ({
1139
+ ...message,
1140
+ content: [{ type: "text", text: `Reply ${index + 1}` }],
1141
+ })),
1142
+ })
1143
+ }
1144
+ />
1145
+ </AssistantRuntimeProvider>
1146
+ );
1147
+ });
1148
+ render(<SwitchingThread />);
1149
+ const viewport = getViewport();
1150
+ await waitFor(() =>
1151
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1152
+ messages.length,
1153
+ ),
1154
+ );
1155
+ await act(() => {
1156
+ viewport.scrollTop = getMaxScrollTop(viewport);
1157
+ fireEvent.scroll(viewport);
1158
+ });
1159
+
1160
+ await click(addTrigger({ "aria-expanded": "false" }));
1161
+ await act(async () => {
1162
+ fireEvent.click(screen.getByTestId("switch"));
1163
+ });
1164
+ await growContent();
1165
+
1166
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1167
+ });
1168
+ });
1169
+
1170
+ it.each([
1171
+ { label: "pointerdown", make: () => new Event("pointerdown") },
1172
+ { label: "wheel", make: () => new WheelEvent("wheel") },
1173
+ { label: "touchstart", make: () => new Event("touchstart") },
1174
+ {
1175
+ label: "Enter keydown",
1176
+ make: () => new KeyboardEvent("keydown", { key: "Enter" }),
1177
+ },
1178
+ {
1179
+ label: "Space keydown",
1180
+ make: () => new KeyboardEvent("keydown", { key: " " }),
1181
+ },
1182
+ ...["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End"].map(
1183
+ (key) => ({
1184
+ label: `${key} keydown`,
1185
+ make: () => new KeyboardEvent("keydown", { key }),
1186
+ }),
1187
+ ),
1188
+ ])(
1189
+ "drops pending bottom-scroll intent after a $label in a thread that cannot scroll",
1190
+ async ({ make }) => {
1191
+ // the viewport never overflows, so handleScroll keeps the intent alive
1192
+ forceShortViewportMeasurement = true;
1193
+
1194
+ render(
1195
+ <AsyncRuntimeProvider>
1196
+ <Thread />
1197
+ </AsyncRuntimeProvider>,
1198
+ );
1199
+
1200
+ await waitFor(() => {
1201
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1202
+ messages.length,
1203
+ );
1204
+ });
1205
+ await new Promise((resolve) => setTimeout(resolve, 0));
1206
+ expect(getViewport().scrollTop).toBe(0);
1207
+
1208
+ // the user activates something in the thread, e.g. a collapsible tool call
1209
+ await act(() => {
1210
+ getViewport().dispatchEvent(make());
1211
+ });
1212
+
1213
+ // that activation grows the content
1214
+ forceShortViewportMeasurement = false;
1215
+ await act(notifyResizeObservers);
1216
+
1217
+ expect(getViewport().scrollTop).toBe(0);
1218
+ },
1219
+ );
1220
+
1221
+ it.each([
1222
+ { label: "pointerdown", make: () => new Event("pointerdown") },
1223
+ { label: "wheel", make: () => new WheelEvent("wheel") },
1224
+ { label: "touchstart", make: () => new Event("touchstart") },
1225
+ {
1226
+ label: "Enter keydown",
1227
+ make: () => new KeyboardEvent("keydown", { key: "Enter" }),
1228
+ },
1229
+ {
1230
+ label: "Space keydown",
1231
+ make: () => new KeyboardEvent("keydown", { key: " " }),
1232
+ },
1233
+ {
1234
+ label: "ArrowDown keydown",
1235
+ make: () => new KeyboardEvent("keydown", { key: "ArrowDown" }),
1236
+ },
1237
+ ])(
1238
+ "cancels the frame a pending bottom scroll queued when a $label arrives first",
1239
+ async ({ make }) => {
1240
+ let nextFrameId = 0;
1241
+ let pendingFrame: {
1242
+ id: number;
1243
+ callback: FrameRequestCallback;
1244
+ } | null = null;
1245
+ const originalRequestAnimationFrame = globalThis.requestAnimationFrame;
1246
+ const originalCancelAnimationFrame = globalThis.cancelAnimationFrame;
1247
+
1248
+ vi.stubGlobal(
1249
+ "requestAnimationFrame",
1250
+ (callback: FrameRequestCallback) => {
1251
+ const id = ++nextFrameId;
1252
+ pendingFrame = { id, callback };
1253
+ return id;
1254
+ },
1255
+ );
1256
+ vi.stubGlobal(
1257
+ "cancelAnimationFrame",
1258
+ vi.fn((id: number) => {
1259
+ if (pendingFrame?.id === id) pendingFrame = null;
1260
+ }),
1261
+ );
1262
+
1263
+ try {
1264
+ render(
1265
+ <SyncRuntimeProvider>
1266
+ <BottomAnchorThread />
1267
+ </SyncRuntimeProvider>,
1268
+ );
1269
+
1270
+ const viewport = getViewport();
1271
+ await waitFor(() => {
1272
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1273
+ messages.length,
1274
+ );
1275
+ expect(pendingFrame).not.toBeNull();
1276
+ });
1277
+
1278
+ // the gesture lands before the queued frame runs
1279
+ await act(() => {
1280
+ viewport.dispatchEvent(make());
1281
+ });
1282
+
1283
+ // clearing the ref alone would leave this frame to re-plant the intent
1284
+ expect(pendingFrame).toBeNull();
1285
+ } finally {
1286
+ vi.stubGlobal("requestAnimationFrame", originalRequestAnimationFrame);
1287
+ vi.stubGlobal("cancelAnimationFrame", originalCancelAnimationFrame);
1288
+ }
1289
+ },
1290
+ );
1291
+
1292
+ it.each(["Shift", "Control", "Meta", "Tab", "Escape", "a"])(
1293
+ "keeps pending bottom-scroll intent through a %s press",
1294
+ async (key) => {
1295
+ forceShortViewportMeasurement = true;
1296
+
1297
+ render(
1298
+ <AsyncRuntimeProvider>
1299
+ <Thread />
1300
+ </AsyncRuntimeProvider>,
1301
+ );
1302
+
1303
+ await waitFor(() => {
1304
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1305
+ messages.length,
1306
+ );
1307
+ });
1308
+ await new Promise((resolve) => setTimeout(resolve, 0));
1309
+
1310
+ // neither an activation key nor consumed by a text field
1311
+ await act(() => {
1312
+ getViewport().dispatchEvent(new KeyboardEvent("keydown", { key }));
1313
+ });
1314
+
1315
+ forceShortViewportMeasurement = false;
1316
+ await act(notifyResizeObservers);
1317
+
1318
+ expect(getViewport().scrollTop).toBe(getMaxScrollTop(getViewport()));
1319
+ },
1320
+ );
1321
+
1322
+ it.each([" ", "ArrowDown"])(
1323
+ "keeps pending bottom-scroll intent through a %s press in the composer",
1324
+ async (key) => {
1325
+ forceShortViewportMeasurement = true;
1326
+
1327
+ render(
1328
+ <AsyncRuntimeProvider>
1329
+ <Thread />
1330
+ </AsyncRuntimeProvider>,
1331
+ );
1332
+
1333
+ await waitFor(() => {
1334
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1335
+ messages.length,
1336
+ );
1337
+ });
1338
+ await new Promise((resolve) => setTimeout(resolve, 0));
1339
+
1340
+ await act(() => {
1341
+ screen
1342
+ .getByTestId("composer")
1343
+ .dispatchEvent(new KeyboardEvent("keydown", { key, bubbles: true }));
1344
+ });
1345
+
1346
+ forceShortViewportMeasurement = false;
1347
+ await act(notifyResizeObservers);
1348
+
1349
+ expect(getViewport().scrollTop).toBe(getMaxScrollTop(getViewport()));
1350
+ },
1351
+ );
1352
+
1353
+ it.each([
1354
+ { label: "a checkbox", testid: "checkbox" },
1355
+ { label: "a non-editable element", testid: "readonly-island" },
1356
+ ])(
1357
+ "drops pending bottom-scroll intent when a key activates $label",
1358
+ async ({ testid }) => {
1359
+ forceShortViewportMeasurement = true;
1360
+
1361
+ render(
1362
+ <AsyncRuntimeProvider>
1363
+ <Thread />
1364
+ </AsyncRuntimeProvider>,
1365
+ );
1366
+
1367
+ await waitFor(() => {
1368
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1369
+ messages.length,
1370
+ );
1371
+ });
1372
+ await new Promise((resolve) => setTimeout(resolve, 0));
1373
+
1374
+ await act(() => {
1375
+ screen
1376
+ .getByTestId(testid)
1377
+ .dispatchEvent(
1378
+ new KeyboardEvent("keydown", { key: " ", bubbles: true }),
1379
+ );
1380
+ });
1381
+
1382
+ forceShortViewportMeasurement = false;
1383
+ await act(notifyResizeObservers);
1384
+
1385
+ expect(getViewport().scrollTop).toBe(0);
1386
+ },
1387
+ );
1388
+
1389
+ it("cancels a queued bottom scroll when the user scrolls up", async () => {
1390
+ let nextFrameId = 0;
1391
+ let pendingFrame: {
1392
+ id: number;
1393
+ callback: FrameRequestCallback;
1394
+ } | null = null;
1395
+ const originalRequestAnimationFrame = globalThis.requestAnimationFrame;
1396
+ const originalCancelAnimationFrame = globalThis.cancelAnimationFrame;
1397
+
1398
+ vi.stubGlobal("requestAnimationFrame", (callback: FrameRequestCallback) => {
1399
+ const id = ++nextFrameId;
1400
+ pendingFrame = { id, callback };
1401
+ return id;
1402
+ });
1403
+ const cancelAnimationFrame = vi.fn((id: number) => {
1404
+ if (pendingFrame?.id === id) pendingFrame = null;
1405
+ });
1406
+ vi.stubGlobal("cancelAnimationFrame", cancelAnimationFrame);
1407
+
1408
+ try {
1409
+ render(
1410
+ <SyncRuntimeProvider>
1411
+ <BottomAnchorThread />
1412
+ </SyncRuntimeProvider>,
1413
+ );
1414
+
1415
+ const viewport = getViewport();
1416
+ await waitFor(() => {
1417
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1418
+ messages.length,
1419
+ );
1420
+ expect(pendingFrame).not.toBeNull();
1421
+ });
1422
+
1423
+ let scrollTopAfterLeave = 0;
1424
+ await act(() => {
1425
+ viewport.scrollTop = getMaxScrollTop(viewport);
1426
+ viewport.dispatchEvent(new Event("scroll"));
1427
+ viewport.scrollTop -= 80;
1428
+ scrollTopAfterLeave = viewport.scrollTop;
1429
+ viewport.dispatchEvent(new Event("scroll"));
1430
+ });
1431
+
1432
+ const frame = pendingFrame as {
1433
+ id: number;
1434
+ callback: FrameRequestCallback;
1435
+ } | null;
1436
+ if (frame) {
1437
+ await act(() => {
1438
+ frame.callback(performance.now());
1439
+ });
1440
+ }
1441
+
1442
+ expect(viewport.scrollTop).toBe(scrollTopAfterLeave);
1443
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
1444
+ expect(cancelAnimationFrame).toHaveBeenCalledWith(expect.any(Number));
1445
+ } finally {
1446
+ vi.stubGlobal("requestAnimationFrame", originalRequestAnimationFrame);
1447
+ vi.stubGlobal("cancelAnimationFrame", originalCancelAnimationFrame);
1448
+ }
1449
+ });
1450
+
1451
+ it("does not resume bottom follow after a stable-height user scroll-up", async () => {
1452
+ render(
1453
+ <SyncRuntimeProvider>
1454
+ <BottomAnchorThread />
1455
+ </SyncRuntimeProvider>,
1456
+ );
1457
+
1458
+ const viewport = getViewport();
1459
+ await waitFor(() => {
1460
+ expect(messageRows()).toHaveLength(messages.length);
1461
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1462
+ });
1463
+
1464
+ await act(() => {
1465
+ viewport.scrollTop = viewport.scrollTop - 80;
1466
+ viewport.dispatchEvent(new Event("scroll"));
1467
+ });
1468
+
1469
+ const scrollTopAfterLeave = viewport.scrollTop;
1470
+ viewportMeasurementOffset += 200;
1471
+ await act(notifyResizeObservers);
1472
+
1473
+ expect(viewport.scrollTop).toBe(scrollTopAfterLeave);
1474
+ expect(viewport.scrollTop).toBeLessThan(getMaxScrollTop(viewport));
1475
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
1476
+ });
1477
+
1478
+ it("starts following when auto-scroll is enabled at the bottom", async () => {
1479
+ const view = render(
1480
+ <SyncRuntimeProvider>
1481
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
1482
+ </SyncRuntimeProvider>,
1483
+ );
1484
+
1485
+ const viewport = getViewport();
1486
+ await waitFor(() => {
1487
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1488
+ messages.length,
1489
+ );
1490
+ });
1491
+
1492
+ await act(() => {
1493
+ viewport.scrollTop = getMaxScrollTop(viewport);
1494
+ viewport.dispatchEvent(new Event("scroll"));
1495
+ });
1496
+
1497
+ await view.rerender(
1498
+ <SyncRuntimeProvider>
1499
+ <Thread autoScroll scrollToBottomOnInitialize={false} />
1500
+ </SyncRuntimeProvider>,
1501
+ );
1502
+
1503
+ viewportMeasurementOffset += 200;
1504
+ await act(notifyResizeObservers);
1505
+
1506
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1507
+ });
1508
+
1509
+ it("preserves bottom follow on initial mount when initialize scrolling is disabled", async () => {
1510
+ render(
1511
+ <SyncRuntimeProvider>
1512
+ <ThreadPrimitiveRoot>
1513
+ <ThreadPrimitiveViewport
1514
+ autoScroll
1515
+ data-testid="viewport"
1516
+ scrollToBottomOnInitialize={false}
1517
+ turnAnchor="top"
1518
+ >
1519
+ {messages.map((_, index) => (
1520
+ <div key={index} data-testid="thread-message" />
1521
+ ))}
1522
+ </ThreadPrimitiveViewport>
1523
+ </ThreadPrimitiveRoot>
1524
+ </SyncRuntimeProvider>,
1525
+ );
1526
+
1527
+ const viewport = getViewport();
1528
+ await waitFor(() => {
1529
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1530
+ messages.length,
1531
+ );
1532
+ });
1533
+
1534
+ viewportMeasurementOffset += 200;
1535
+ await act(notifyResizeObservers);
1536
+
1537
+ expect(viewport.scrollTop).toBe(getMaxScrollTop(viewport));
1538
+ });
1539
+
1540
+ it("does not jump down when auto-scroll is enabled away from the bottom", async () => {
1541
+ const view = render(
1542
+ <SyncRuntimeProvider>
1543
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
1544
+ </SyncRuntimeProvider>,
1545
+ );
1546
+
1547
+ const viewport = getViewport();
1548
+ await waitFor(() => {
1549
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1550
+ messages.length,
1551
+ );
1552
+ });
1553
+
1554
+ await act(() => {
1555
+ viewport.scrollTop = getMaxScrollTop(viewport) - 80;
1556
+ viewport.dispatchEvent(new Event("scroll"));
1557
+ });
1558
+ const scrollTopBeforeEnable = viewport.scrollTop;
1559
+
1560
+ await view.rerender(
1561
+ <SyncRuntimeProvider>
1562
+ <Thread autoScroll scrollToBottomOnInitialize={false} />
1563
+ </SyncRuntimeProvider>,
1564
+ );
1565
+
1566
+ viewportMeasurementOffset += 200;
1567
+ await act(notifyResizeObservers);
1568
+
1569
+ expect(viewport.scrollTop).toBe(scrollTopBeforeEnable);
1570
+ expect(viewport.scrollTop).toBeLessThan(getMaxScrollTop(viewport));
1571
+ });
1572
+
1573
+ it("defers auto-scroll to an active top anchor only while the run is active", async () => {
1574
+ let releaseRun!: () => void;
1575
+ const runGate = new Promise<void>((resolve) => {
1576
+ releaseRun = resolve;
1577
+ });
1578
+ const deferredAdapter: ChatModelAdapter = {
1579
+ async *run() {
1580
+ await runGate;
1581
+ yield { content: [{ type: "text", text: "done" }] };
1582
+ },
1583
+ };
1584
+ const scrollToSpy = vi.spyOn(HTMLElement.prototype, "scrollTo");
1585
+
1586
+ let runtime: ReturnType<typeof useLocalRuntime> | null = null;
1587
+ const Harness = defineComponent(() => {
1588
+ const mountedRuntime = useLocalRuntime(deferredAdapter, {
1589
+ initialMessages: messages,
1590
+ });
1591
+ runtime = mountedRuntime;
1592
+ return () => (
1593
+ <AssistantRuntimeProvider runtime={mountedRuntime}>
1594
+ <Thread autoScroll scrollToBottomOnInitialize={false} />
1595
+ <RunState />
1596
+ </AssistantRuntimeProvider>
1597
+ );
1598
+ });
1599
+
1600
+ render(<Harness />);
1601
+
1602
+ await act(() => {
1603
+ void runtime!.thread.append({
1604
+ role: "user",
1605
+ content: [{ type: "text", text: "hello" }],
1606
+ });
1607
+ });
1608
+
1609
+ await waitFor(() => {
1610
+ expect(
1611
+ document.querySelector("[data-aui-top-anchor-user]"),
1612
+ ).not.toBeNull();
1613
+ });
1614
+
1615
+ scrollToSpy.mockClear();
1616
+ viewportMeasurementOffset = 1;
1617
+ await act(notifyResizeObservers);
1618
+
1619
+ expect(
1620
+ scrollToSpy.mock.calls.map(
1621
+ (call) => (call[0] as ScrollToOptions).behavior,
1622
+ ),
1623
+ ).not.toContain("instant");
1624
+
1625
+ await act(async () => {
1626
+ releaseRun();
1627
+ });
1628
+ await waitFor(() => {
1629
+ expect(screen.getByTestId("run-state").textContent).toBe("false");
1630
+ });
1631
+ expect(document.querySelector("[data-aui-top-anchor-user]")).not.toBeNull();
1632
+
1633
+ const viewport = getViewport();
1634
+ await act(() => {
1635
+ viewport.scrollTop = getMaxScrollTop(viewport);
1636
+ viewport.dispatchEvent(new Event("scroll"));
1637
+ });
1638
+ scrollToSpy.mockClear();
1639
+ viewportMeasurementOffset = 2;
1640
+ await act(notifyResizeObservers);
1641
+
1642
+ expect(
1643
+ scrollToSpy.mock.calls.map(
1644
+ (call) => (call[0] as ScrollToOptions).behavior,
1645
+ ),
1646
+ ).toContain("instant");
1647
+
1648
+ scrollToSpy.mockRestore();
1649
+ });
1650
+
1651
+ it("does not scroll initial messages when initialize scrolling is disabled", async () => {
1652
+ render(
1653
+ <SyncRuntimeProvider>
1654
+ <Thread autoScroll={false} scrollToBottomOnInitialize={false} />
1655
+ </SyncRuntimeProvider>,
1656
+ );
1657
+
1658
+ await waitFor(() => {
1659
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(
1660
+ messages.length,
1661
+ );
1662
+ });
1663
+
1664
+ expect(getViewport().scrollTop).toBe(0);
1665
+ viewportMeasurementOffset += 200;
1666
+ await act(notifyResizeObservers);
1667
+ expect(screen.getByTestId("is-at-bottom").textContent).toBe("false");
1668
+ });
1669
+
1670
+ describe("earlier messages", () => {
1671
+ const pagedMessages: ThreadMessageLike[] = messages.map(
1672
+ (message, index) => ({
1673
+ ...message,
1674
+ id: `message-${index}`,
1675
+ }),
1676
+ );
1677
+
1678
+ type PagedThreadProps = {
1679
+ follow?: boolean;
1680
+ running?: boolean;
1681
+ /** Runs in the same update that prepends the page. */
1682
+ onPrepend?: () => void;
1683
+ };
1684
+
1685
+ const PagedThread = defineComponent(
1686
+ (props: PagedThreadProps) => {
1687
+ const loaded = shallowRef(pagedMessages.slice(4));
1688
+ const runtime = useExternalStoreRuntime<ThreadMessageLike>(() => ({
1689
+ messages: loaded.value,
1690
+ convertMessage: (message) => message,
1691
+ onNew: async () => {},
1692
+ isRunning: props.running ?? false,
1693
+ hasEarlier: loaded.value.length < pagedMessages.length,
1694
+ onLoadEarlier: async () => {
1695
+ props.onPrepend?.();
1696
+ loaded.value = pagedMessages;
1697
+ },
1698
+ }));
1699
+
1700
+ return () => (
1701
+ <AssistantRuntimeProvider runtime={runtime}>
1702
+ {props.follow ? <BottomAnchorThread /> : <Thread />}
1703
+ <RunState />
1704
+ <ThreadPrimitiveLoadEarlier data-testid="load-earlier" />
1705
+ </AssistantRuntimeProvider>
1706
+ );
1707
+ },
1708
+ { props: ["follow", "running", "onPrepend"] },
1709
+ );
1710
+
1711
+ const renderAt = async (
1712
+ scrollTop: number,
1713
+ { follow = false, onPrepend }: PagedThreadProps = {},
1714
+ ) => {
1715
+ render(<PagedThread follow={follow} {...(onPrepend && { onPrepend })} />);
1716
+ await waitFor(() => {
1717
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(4);
1718
+ expect(getViewport().scrollTop).toBe(getMaxScrollTop(getViewport()));
1719
+ });
1720
+ await act(() => {
1721
+ getViewport().scrollTop = scrollTop;
1722
+ fireEvent.scroll(getViewport());
1723
+ });
1724
+ };
1725
+
1726
+ const loadEarlier = async () => {
1727
+ await act(async () => {
1728
+ fireEvent.click(screen.getByTestId("load-earlier"));
1729
+ });
1730
+ await waitFor(() =>
1731
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(8),
1732
+ );
1733
+ await act(notifyResizeObservers);
1734
+ };
1735
+
1736
+ it("keeps the rows a reader scrolled to in place when an earlier page is prepended", async () => {
1737
+ await renderAt(0);
1738
+
1739
+ await loadEarlier();
1740
+
1741
+ expect(getViewport().scrollTop).toBe(4 * 80);
1742
+ expect(
1743
+ (screen.getByTestId("load-earlier") as HTMLButtonElement).disabled,
1744
+ ).toBe(true);
1745
+ });
1746
+
1747
+ it.each([false, true])(
1748
+ "keeps a reader at the bottom at the bottom (auto-follow %s)",
1749
+ async (follow) => {
1750
+ await renderAt(4 * 80 - 100, { follow });
1751
+
1752
+ await loadEarlier();
1753
+
1754
+ expect(getViewport().scrollTop).toBe(8 * 80 - 100);
1755
+ },
1756
+ );
1757
+
1758
+ it("does not count growth below the reader in the same update as part of the page", async () => {
1759
+ await renderAt(100, {
1760
+ onPrepend: () => {
1761
+ viewportMeasurementOffset = 40;
1762
+ },
1763
+ });
1764
+
1765
+ await loadEarlier();
1766
+
1767
+ expect(getViewport().scrollTop).toBe(4 * 80 + 100);
1768
+ });
1769
+
1770
+ it("holds the rows in place while the page above settles, until the reader's next gesture", async () => {
1771
+ await renderAt(0);
1772
+ await loadEarlier();
1773
+ expect(getViewport().scrollTop).toBe(4 * 80);
1774
+
1775
+ messageHeights.set("message-3", 120);
1776
+ await act(notifyResizeObservers);
1777
+ expect(getViewport().scrollTop).toBe(4 * 80 + 40);
1778
+
1779
+ fireEvent.wheel(getViewport());
1780
+ messageHeights.set("message-2", 120);
1781
+ await act(notifyResizeObservers);
1782
+ expect(getViewport().scrollTop).toBe(4 * 80 + 40);
1783
+ });
1784
+
1785
+ it("lets go of the held rows when a run starts, so the new turn can take the viewport", async () => {
1786
+ const { rerender } = render(<PagedThread />);
1787
+ await waitFor(() => {
1788
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(4);
1789
+ expect(getViewport().scrollTop).toBe(getMaxScrollTop(getViewport()));
1790
+ });
1791
+ await act(() => {
1792
+ getViewport().scrollTop = 0;
1793
+ fireEvent.scroll(getViewport());
1794
+ });
1795
+ await loadEarlier();
1796
+
1797
+ await act(async () => {
1798
+ await rerender(<PagedThread running />);
1799
+ });
1800
+ await waitFor(() =>
1801
+ expect(screen.getByTestId("run-state").textContent).toBe("true"),
1802
+ );
1803
+ messageHeights.set("message-3", 120);
1804
+ await act(notifyResizeObservers);
1805
+
1806
+ expect(getViewport().scrollTop).toBe(4 * 80);
1807
+ });
1808
+
1809
+ it("lets go of the held rows once something else scrolls the viewport", async () => {
1810
+ await renderAt(0);
1811
+ await loadEarlier();
1812
+
1813
+ await act(() => {
1814
+ getViewport().scrollTo({ top: 100 });
1815
+ });
1816
+ messageHeights.set("message-3", 120);
1817
+ await act(notifyResizeObservers);
1818
+
1819
+ expect(getViewport().scrollTop).toBe(100);
1820
+ });
1821
+
1822
+ it("holds the row the reader sees, not the old first message, while content between them settles", async () => {
1823
+ await renderAt(100);
1824
+ await loadEarlier();
1825
+ expect(getViewport().scrollTop).toBe(4 * 80 + 100);
1826
+
1827
+ messageHeights.set("message-4", 120);
1828
+ await act(notifyResizeObservers);
1829
+ expect(getViewport().scrollTop).toBe(4 * 80 + 100 + 40);
1830
+ });
1831
+
1832
+ it("does not treat a thread switch as a page, even when the new thread repeats the old first message", async () => {
1833
+ const SwitchingThread = defineComponent(() => {
1834
+ const thread = shallowRef({
1835
+ id: "a",
1836
+ messages: pagedMessages.slice(4),
1837
+ });
1838
+ const runtime = useExternalStoreRuntime<ThreadMessageLike>(() => ({
1839
+ messages: thread.value.messages,
1840
+ convertMessage: (message) => message,
1841
+ onNew: async () => {},
1842
+ adapters: {
1843
+ threadList: {
1844
+ threadId: thread.value.id,
1845
+ threads: [
1846
+ { id: "a", status: "regular" },
1847
+ { id: "b", status: "regular" },
1848
+ ],
1849
+ },
1850
+ },
1851
+ }));
1852
+
1853
+ return () => (
1854
+ <AssistantRuntimeProvider runtime={runtime}>
1855
+ <Thread scrollToBottomOnThreadSwitch={false} />
1856
+ <button
1857
+ type="button"
1858
+ data-testid="switch"
1859
+ onClick={() =>
1860
+ (thread.value = { id: "b", messages: pagedMessages.slice(3) })
1861
+ }
1862
+ />
1863
+ </AssistantRuntimeProvider>
1864
+ );
1865
+ });
1866
+
1867
+ render(<SwitchingThread />);
1868
+ await waitFor(() =>
1869
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(4),
1870
+ );
1871
+ await act(() => {
1872
+ getViewport().scrollTop = 100;
1873
+ fireEvent.scroll(getViewport());
1874
+ });
1875
+
1876
+ await act(async () => {
1877
+ fireEvent.click(screen.getByTestId("switch"));
1878
+ });
1879
+ await waitFor(() =>
1880
+ expect(screen.getAllByTestId("thread-message")).toHaveLength(5),
1881
+ );
1882
+ await act(notifyResizeObservers);
1883
+
1884
+ expect(getViewport().scrollTop).toBe(100);
1885
+ });
1886
+ });
1887
+ });