@assistant-ui/react 0.15.29 → 0.15.31

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 (370) hide show
  1. package/dist/cloud-renderer/CloudRendererHost.d.ts.map +1 -1
  2. package/dist/cloud-renderer/CloudRendererHost.js +1 -2
  3. package/dist/cloud-renderer/CloudRendererHost.js.map +1 -1
  4. package/dist/context/providers/AttachmentByIndexProvider.js +1 -2
  5. package/dist/context/providers/ChainOfThoughtByIndicesProvider.js +1 -2
  6. package/dist/context/providers/MessageByIndexProvider.js +1 -2
  7. package/dist/context/providers/PartByIndexProvider.js +1 -2
  8. package/dist/context/providers/SuggestionByIndexProvider.js +1 -2
  9. package/dist/context/providers/TextMessagePartProvider.js +1 -2
  10. package/dist/context/providers/ThreadListItemProvider.js +1 -2
  11. package/dist/context/stores/ThreadViewport.d.ts +8 -0
  12. package/dist/context/stores/ThreadViewport.d.ts.map +1 -1
  13. package/dist/context/stores/ThreadViewport.js +24 -8
  14. package/dist/context/stores/ThreadViewport.js.map +1 -1
  15. package/dist/index.d.ts +10 -9
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -9
  18. package/dist/internal.d.ts +1 -1
  19. package/dist/internal.d.ts.map +1 -1
  20. package/dist/internal.js +1 -1
  21. package/dist/internal.js.map +1 -1
  22. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts +1 -1
  23. package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
  24. package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
  25. package/dist/primitives/actionBar/ActionBarCopy.d.ts +1 -1
  26. package/dist/primitives/actionBar/ActionBarEdit.d.ts +1 -1
  27. package/dist/primitives/actionBar/ActionBarExportMarkdown.d.ts +1 -1
  28. package/dist/primitives/actionBar/ActionBarFeedbackNegative.d.ts +1 -1
  29. package/dist/primitives/actionBar/ActionBarFeedbackPositive.d.ts +1 -1
  30. package/dist/primitives/actionBar/ActionBarReload.d.ts +1 -1
  31. package/dist/primitives/actionBar/ActionBarRoot.d.ts +1 -1
  32. package/dist/primitives/actionBar/ActionBarSpeak.d.ts +1 -1
  33. package/dist/primitives/actionBar/ActionBarStopSpeaking.d.ts +1 -1
  34. package/dist/primitives/attachment/AttachmentRemove.d.ts +1 -1
  35. package/dist/primitives/attachment/AttachmentRoot.d.ts +1 -1
  36. package/dist/primitives/attachment/AttachmentThumb.d.ts +1 -1
  37. package/dist/primitives/branchPicker/BranchPickerNext.d.ts +1 -1
  38. package/dist/primitives/branchPicker/BranchPickerPrevious.d.ts +1 -1
  39. package/dist/primitives/branchPicker/BranchPickerRoot.d.ts +1 -1
  40. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.d.ts +1 -1
  41. package/dist/primitives/chainOfThought/ChainOfThoughtParts.js +1 -2
  42. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.d.ts +1 -1
  43. package/dist/primitives/composer/ComposerAddAttachment.d.ts +1 -1
  44. package/dist/primitives/composer/ComposerAttachments.js +1 -2
  45. package/dist/primitives/composer/ComposerCancel.d.ts +1 -1
  46. package/dist/primitives/composer/ComposerDictate.d.ts +1 -1
  47. package/dist/primitives/composer/ComposerDictationTranscript.d.ts +1 -1
  48. package/dist/primitives/composer/ComposerIf.js +1 -2
  49. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  50. package/dist/primitives/composer/ComposerInput.js +3 -3
  51. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  52. package/dist/primitives/composer/ComposerQueue.js +1 -2
  53. package/dist/primitives/composer/ComposerQuote.d.ts +3 -3
  54. package/dist/primitives/composer/ComposerResume.d.ts +23 -0
  55. package/dist/primitives/composer/ComposerResume.d.ts.map +1 -0
  56. package/dist/primitives/composer/ComposerResume.js +34 -0
  57. package/dist/primitives/composer/ComposerResume.js.map +1 -0
  58. package/dist/primitives/composer/ComposerRoot.d.ts +1 -1
  59. package/dist/primitives/composer/ComposerSend.d.ts +1 -1
  60. package/dist/primitives/composer/ComposerStopDictation.d.ts +1 -1
  61. package/dist/primitives/composer/trigger/TriggerPopover.d.ts +1 -1
  62. package/dist/primitives/composer/trigger/TriggerPopoverBack.d.ts +1 -1
  63. package/dist/primitives/composer/trigger/TriggerPopoverCategories.d.ts +2 -2
  64. package/dist/primitives/composer/trigger/TriggerPopoverItems.d.ts +2 -2
  65. package/dist/primitives/composer/trigger/index.d.ts +1 -1
  66. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts +6 -2
  67. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  68. package/dist/primitives/composer/trigger/triggerSelectionResource.js +6 -3
  69. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  70. package/dist/primitives/composer.d.ts +1 -0
  71. package/dist/primitives/composer.d.ts.map +1 -1
  72. package/dist/primitives/composer.js +3 -1
  73. package/dist/primitives/composer.js.map +1 -1
  74. package/dist/primitives/error/ErrorMessage.d.ts +1 -1
  75. package/dist/primitives/error/ErrorRoot.d.ts +1 -1
  76. package/dist/primitives/message/MessageAttachments.js +1 -2
  77. package/dist/primitives/message/MessageRoot.d.ts +1 -1
  78. package/dist/primitives/messagePart/MessagePartImage.d.ts +1 -1
  79. package/dist/primitives/messagePart/MessagePartInProgress.js +1 -2
  80. package/dist/primitives/messagePart/MessagePartText.d.ts +1 -1
  81. package/dist/primitives/queueItem/QueueItemRemove.d.ts +1 -1
  82. package/dist/primitives/queueItem/QueueItemSteer.d.ts +1 -1
  83. package/dist/primitives/queueItem/QueueItemText.d.ts +1 -1
  84. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.d.ts +1 -1
  85. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts +1 -1
  86. package/dist/primitives/suggestion/SuggestionDescription.d.ts +1 -1
  87. package/dist/primitives/suggestion/SuggestionTitle.d.ts +1 -1
  88. package/dist/primitives/suggestion/SuggestionTrigger.d.ts +1 -1
  89. package/dist/primitives/thread/ThreadLoadEarlier.d.ts +1 -1
  90. package/dist/primitives/thread/ThreadMessages.js +1 -2
  91. package/dist/primitives/thread/ThreadRoot.d.ts +1 -1
  92. package/dist/primitives/thread/ThreadScrollToBottom.d.ts +1 -1
  93. package/dist/primitives/thread/ThreadSuggestion.d.ts +1 -1
  94. package/dist/primitives/thread/ThreadSuggestions.js +1 -2
  95. package/dist/primitives/thread/ThreadViewport.d.ts +1 -1
  96. package/dist/primitives/thread/ThreadViewportFooter.d.ts +1 -1
  97. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  98. package/dist/primitives/thread/useThreadViewportAutoScroll.js +36 -293
  99. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  100. package/dist/primitives/threadList/ThreadListItems.js +1 -2
  101. package/dist/primitives/threadList/ThreadListLoadMore.d.ts +1 -1
  102. package/dist/primitives/threadList/ThreadListNew.d.ts +1 -1
  103. package/dist/primitives/threadList/ThreadListRoot.d.ts +1 -1
  104. package/dist/primitives/threadListFocusGroup.d.ts +1 -1
  105. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
  106. package/dist/primitives/threadListItem/ThreadListItemArchive.d.ts +1 -1
  107. package/dist/primitives/threadListItem/ThreadListItemDelete.d.ts +1 -1
  108. package/dist/primitives/threadListItem/ThreadListItemRoot.d.ts +1 -1
  109. package/dist/primitives/threadListItem/ThreadListItemTitle.js +1 -2
  110. package/dist/primitives/threadListItem/ThreadListItemTrigger.d.ts +1 -1
  111. package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts +2 -2
  112. package/dist/primitives/threadListItem/ThreadListItemUnarchive.d.ts.map +1 -1
  113. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js +2 -2
  114. package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
  115. package/dist/unstable/useComposerInput.js +1 -1
  116. package/dist/unstable/useComposerInput.js.map +1 -1
  117. package/dist/unstable/useComposerInputHistory.d.ts.map +1 -1
  118. package/dist/unstable/useComposerInputHistory.js +2 -2
  119. package/dist/unstable/useComposerInputHistory.js.map +1 -1
  120. package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -1
  121. package/dist/unstable/useLiveCompletionAdapter.js +3 -2
  122. package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
  123. package/dist/utils/Primitive.d.ts +17 -17
  124. package/dist/utils/createActionButton.d.ts +1 -1
  125. package/package.json +14 -14
  126. package/src/adapters/OpenAIRealtimeVoiceAdapter.test.ts +14 -1
  127. package/src/cloud-renderer/CloudRendererHost.tsx +4 -2
  128. package/src/context/stores/ThreadViewport.ts +36 -8
  129. package/src/index.ts +11 -8
  130. package/src/internal.ts +1 -1
  131. package/src/legacy-runtime/AssistantRuntimeProvider.tsx +1 -1
  132. package/src/model-context/makeAssistantVisible.test.tsx +25 -37
  133. package/src/primitives/actionBar/ActionBarCopy.test.tsx +57 -0
  134. package/src/primitives/actionBar/ActionBarExportMarkdown.test.tsx +24 -5
  135. package/src/primitives/assistantModal/AssistantModalRoot.test.tsx +1 -1
  136. package/src/primitives/composer/ComposerInput.focus-on-run-start.test.tsx +1 -1
  137. package/src/primitives/composer/ComposerInput.test.tsx +1 -0
  138. package/src/primitives/composer/ComposerInput.text-change.test.tsx +68 -0
  139. package/src/primitives/composer/ComposerInput.thread-scope.test.tsx +82 -0
  140. package/src/primitives/composer/ComposerInput.tsx +5 -4
  141. package/src/primitives/composer/ComposerResume.ts +36 -0
  142. package/src/primitives/composer/trigger/triggerSelectionResource.test.ts +30 -4
  143. package/src/primitives/composer/trigger/triggerSelectionResource.ts +16 -3
  144. package/src/primitives/composer/useAuiState.composer.test.tsx +84 -0
  145. package/src/primitives/composer.ts +1 -0
  146. package/src/primitives/thread/ThreadRoot.test.tsx +1 -1
  147. package/src/primitives/thread/ThreadViewport.thread-scope.test.tsx +51 -0
  148. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +90 -0
  149. package/src/primitives/thread/useThreadViewportAutoScroll.ts +56 -488
  150. package/src/primitives/threadListItem/ThreadListItemUnarchive.test.tsx +52 -0
  151. package/src/primitives/threadListItem/ThreadListItemUnarchive.ts +2 -2
  152. package/src/tests/DataRenderers.test.tsx +1 -1
  153. package/src/tests/MessageParts.loading.test.tsx +1 -1
  154. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +1 -1
  155. package/src/tests/RemoteThreadListRuntime.deferredProvider.test.tsx +4 -2
  156. package/src/tests/RemoteThreadListRuntime.reloadMainThread.test.tsx +4 -2
  157. package/src/tests/generative-ui.primitive.test.tsx +1 -1
  158. package/src/tests/local-runtime-queue.test.tsx +2 -2
  159. package/src/tests/messagePartSwitchRace.test.tsx +2 -2
  160. package/src/tests/messagePartTypeMismatch.test.tsx +1 -1
  161. package/src/tests/threadListItemIsRunning.test.tsx +5 -3
  162. package/src/tests/toolCallTiming.test.tsx +1 -1
  163. package/src/unstable/useComposerInput.test.tsx +1 -1
  164. package/src/unstable/useComposerInput.ts +1 -1
  165. package/src/unstable/useComposerInputHistory.test.tsx +10 -7
  166. package/src/unstable/useComposerInputHistory.thread-scope.test.tsx +123 -0
  167. package/src/unstable/useComposerInputHistory.ts +3 -2
  168. package/src/unstable/useLiveCompletionAdapter.test.tsx +44 -0
  169. package/src/unstable/useLiveCompletionAdapter.ts +3 -2
  170. package/src/unstable/webmcp/convertTools.test.ts +11 -1
  171. package/src/utils/hooks/useMediaQuery.test.tsx +35 -0
  172. package/src/utils/smooth/useSmooth.test.tsx +37 -10
  173. package/dist/context/providers/ChainOfThoughtPartByIndexProvider.d.ts +0 -2
  174. package/dist/context/providers/ChainOfThoughtPartByIndexProvider.d.ts.map +0 -1
  175. package/dist/context/providers/ChainOfThoughtPartByIndexProvider.js +0 -3
  176. package/dist/legacy-runtime/RuntimeAdapter.d.ts +0 -2
  177. package/dist/legacy-runtime/RuntimeAdapter.d.ts.map +0 -1
  178. package/dist/legacy-runtime/RuntimeAdapter.js +0 -2
  179. package/dist/legacy-runtime/cloud/AssistantCloudThreadHistoryAdapter.d.ts +0 -2
  180. package/dist/legacy-runtime/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +0 -1
  181. package/dist/legacy-runtime/cloud/AssistantCloudThreadHistoryAdapter.js +0 -3
  182. package/dist/legacy-runtime/runtime/AssistantRuntime.d.ts +0 -3
  183. package/dist/legacy-runtime/runtime/AssistantRuntime.d.ts.map +0 -1
  184. package/dist/legacy-runtime/runtime/AssistantRuntime.js +0 -2
  185. package/dist/legacy-runtime/runtime/AttachmentRuntime.d.ts +0 -3
  186. package/dist/legacy-runtime/runtime/AttachmentRuntime.d.ts.map +0 -1
  187. package/dist/legacy-runtime/runtime/AttachmentRuntime.js +0 -2
  188. package/dist/legacy-runtime/runtime/ComposerRuntime.d.ts +0 -4
  189. package/dist/legacy-runtime/runtime/ComposerRuntime.d.ts.map +0 -1
  190. package/dist/legacy-runtime/runtime/ComposerRuntime.js +0 -2
  191. package/dist/legacy-runtime/runtime/RuntimeBindings.d.ts +0 -3
  192. package/dist/legacy-runtime/runtime/RuntimeBindings.d.ts.map +0 -1
  193. package/dist/legacy-runtime/runtime/RuntimeBindings.js +0 -0
  194. package/dist/legacy-runtime/runtime/RuntimePathTypes.d.ts +0 -2
  195. package/dist/legacy-runtime/runtime/RuntimePathTypes.d.ts.map +0 -1
  196. package/dist/legacy-runtime/runtime/RuntimePathTypes.js +0 -0
  197. package/dist/legacy-runtime/runtime/ThreadListItemRuntime.d.ts +0 -5
  198. package/dist/legacy-runtime/runtime/ThreadListItemRuntime.d.ts.map +0 -1
  199. package/dist/legacy-runtime/runtime/ThreadListItemRuntime.js +0 -2
  200. package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts +0 -4
  201. package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts.map +0 -1
  202. package/dist/legacy-runtime/runtime/ThreadRuntime.js +0 -2
  203. package/dist/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.d.ts +0 -2
  204. package/dist/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.d.ts.map +0 -1
  205. package/dist/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.js +0 -3
  206. package/dist/legacy-runtime/runtime-cores/adapters/attachment/CloudFileAttachmentAdapter.d.ts +0 -2
  207. package/dist/legacy-runtime/runtime-cores/adapters/attachment/CloudFileAttachmentAdapter.d.ts.map +0 -1
  208. package/dist/legacy-runtime/runtime-cores/adapters/attachment/CloudFileAttachmentAdapter.js +0 -3
  209. package/dist/legacy-runtime/runtime-cores/composer/BaseComposerRuntimeCore.d.ts +0 -2
  210. package/dist/legacy-runtime/runtime-cores/composer/BaseComposerRuntimeCore.d.ts.map +0 -1
  211. package/dist/legacy-runtime/runtime-cores/composer/BaseComposerRuntimeCore.js +0 -2
  212. package/dist/legacy-runtime/runtime-cores/composer/DefaultEditComposerRuntimeCore.d.ts +0 -2
  213. package/dist/legacy-runtime/runtime-cores/composer/DefaultEditComposerRuntimeCore.d.ts.map +0 -1
  214. package/dist/legacy-runtime/runtime-cores/composer/DefaultEditComposerRuntimeCore.js +0 -2
  215. package/dist/legacy-runtime/runtime-cores/composer/DefaultThreadComposerRuntimeCore.d.ts +0 -2
  216. package/dist/legacy-runtime/runtime-cores/composer/DefaultThreadComposerRuntimeCore.d.ts.map +0 -1
  217. package/dist/legacy-runtime/runtime-cores/composer/DefaultThreadComposerRuntimeCore.js +0 -2
  218. package/dist/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.d.ts +0 -2
  219. package/dist/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.d.ts.map +0 -1
  220. package/dist/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.js +0 -0
  221. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.d.ts +0 -2
  222. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.d.ts.map +0 -1
  223. package/dist/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.js +0 -2
  224. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts +0 -2
  225. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts.map +0 -1
  226. package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.js +0 -2
  227. package/dist/legacy-runtime/runtime-cores/core/ComposerRuntimeCore.d.ts +0 -2
  228. package/dist/legacy-runtime/runtime-cores/core/ComposerRuntimeCore.d.ts.map +0 -1
  229. package/dist/legacy-runtime/runtime-cores/core/ComposerRuntimeCore.js +0 -0
  230. package/dist/legacy-runtime/runtime-cores/core/ThreadListRuntimeCore.d.ts +0 -2
  231. package/dist/legacy-runtime/runtime-cores/core/ThreadListRuntimeCore.d.ts.map +0 -1
  232. package/dist/legacy-runtime/runtime-cores/core/ThreadListRuntimeCore.js +0 -0
  233. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts +0 -2
  234. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts.map +0 -1
  235. package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.js +0 -0
  236. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts +0 -2
  237. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.d.ts.map +0 -1
  238. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.js +0 -0
  239. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreRuntimeCore.d.ts +0 -2
  240. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreRuntimeCore.d.ts.map +0 -1
  241. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreRuntimeCore.js +0 -2
  242. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.d.ts +0 -3
  243. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.d.ts.map +0 -1
  244. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.js +0 -2
  245. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts +0 -2
  246. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.d.ts.map +0 -1
  247. package/dist/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.js +0 -2
  248. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageConverter.d.ts +0 -3
  249. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageConverter.d.ts.map +0 -1
  250. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageConverter.js +0 -2
  251. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.d.ts +0 -3
  252. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.d.ts.map +0 -1
  253. package/dist/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.js +0 -2
  254. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.d.ts +0 -2
  255. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.d.ts.map +0 -1
  256. package/dist/legacy-runtime/runtime-cores/external-store/auto-status.js +0 -2
  257. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.d.ts +0 -2
  258. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.d.ts.map +0 -1
  259. package/dist/legacy-runtime/runtime-cores/external-store/createMessageConverter.js +0 -3
  260. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts +0 -2
  261. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.d.ts.map +0 -1
  262. package/dist/legacy-runtime/runtime-cores/external-store/external-message-converter.js +0 -3
  263. package/dist/legacy-runtime/runtime-cores/external-store/useExternalStoreRuntime.d.ts +0 -2
  264. package/dist/legacy-runtime/runtime-cores/external-store/useExternalStoreRuntime.d.ts.map +0 -1
  265. package/dist/legacy-runtime/runtime-cores/external-store/useExternalStoreRuntime.js +0 -3
  266. package/dist/legacy-runtime/runtime-cores/local/ChatModelAdapter.d.ts +0 -2
  267. package/dist/legacy-runtime/runtime-cores/local/ChatModelAdapter.d.ts.map +0 -1
  268. package/dist/legacy-runtime/runtime-cores/local/ChatModelAdapter.js +0 -0
  269. package/dist/legacy-runtime/runtime-cores/local/LocalRuntimeCore.d.ts +0 -2
  270. package/dist/legacy-runtime/runtime-cores/local/LocalRuntimeCore.d.ts.map +0 -1
  271. package/dist/legacy-runtime/runtime-cores/local/LocalRuntimeCore.js +0 -2
  272. package/dist/legacy-runtime/runtime-cores/local/LocalRuntimeOptions.d.ts +0 -4
  273. package/dist/legacy-runtime/runtime-cores/local/LocalRuntimeOptions.d.ts.map +0 -1
  274. package/dist/legacy-runtime/runtime-cores/local/LocalRuntimeOptions.js +0 -2
  275. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.d.ts +0 -3
  276. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.d.ts.map +0 -1
  277. package/dist/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.js +0 -2
  278. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.d.ts +0 -2
  279. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.d.ts.map +0 -1
  280. package/dist/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.js +0 -2
  281. package/dist/legacy-runtime/runtime-cores/local/shouldContinue.d.ts +0 -2
  282. package/dist/legacy-runtime/runtime-cores/local/shouldContinue.d.ts.map +0 -1
  283. package/dist/legacy-runtime/runtime-cores/local/shouldContinue.js +0 -2
  284. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.d.ts +0 -2
  285. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.d.ts.map +0 -1
  286. package/dist/legacy-runtime/runtime-cores/local/useLocalRuntime.js +0 -3
  287. package/dist/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.d.ts +0 -2
  288. package/dist/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.d.ts.map +0 -1
  289. package/dist/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.js +0 -2
  290. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.d.ts +0 -2
  291. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.d.ts.map +0 -1
  292. package/dist/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.js +0 -2
  293. package/dist/legacy-runtime/runtime-cores/remote-thread-list/OptimisticState.d.ts +0 -2
  294. package/dist/legacy-runtime/runtime-cores/remote-thread-list/OptimisticState.d.ts.map +0 -1
  295. package/dist/legacy-runtime/runtime-cores/remote-thread-list/OptimisticState.js +0 -2
  296. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts +0 -2
  297. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts.map +0 -1
  298. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.js +0 -3
  299. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts +0 -2
  300. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts.map +0 -1
  301. package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.js +0 -3
  302. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.d.ts +0 -2
  303. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.d.ts.map +0 -1
  304. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.js +0 -3
  305. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/in-memory.d.ts +0 -2
  306. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/in-memory.d.ts.map +0 -1
  307. package/dist/legacy-runtime/runtime-cores/remote-thread-list/adapter/in-memory.js +0 -2
  308. package/dist/legacy-runtime/runtime-cores/remote-thread-list/types.d.ts +0 -2
  309. package/dist/legacy-runtime/runtime-cores/remote-thread-list/types.d.ts.map +0 -1
  310. package/dist/legacy-runtime/runtime-cores/remote-thread-list/types.js +0 -0
  311. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.d.ts +0 -2
  312. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.d.ts.map +0 -1
  313. package/dist/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.js +0 -3
  314. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.d.ts +0 -4
  315. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.d.ts.map +0 -1
  316. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js +0 -3
  317. package/dist/utils/hooks/useOnResizeContent.d.ts +0 -2
  318. package/dist/utils/hooks/useOnResizeContent.d.ts.map +0 -1
  319. package/dist/utils/hooks/useOnResizeContent.js +0 -21
  320. package/dist/utils/hooks/useOnResizeContent.js.map +0 -1
  321. package/src/context/providers/ChainOfThoughtPartByIndexProvider.tsx +0 -3
  322. package/src/legacy-runtime/RuntimeAdapter.ts +0 -1
  323. package/src/legacy-runtime/cloud/AssistantCloudThreadHistoryAdapter.ts +0 -3
  324. package/src/legacy-runtime/runtime/AssistantRuntime.ts +0 -2
  325. package/src/legacy-runtime/runtime/AttachmentRuntime.ts +0 -7
  326. package/src/legacy-runtime/runtime/ComposerRuntime.ts +0 -20
  327. package/src/legacy-runtime/runtime/RuntimeBindings.ts +0 -12
  328. package/src/legacy-runtime/runtime/RuntimePathTypes.ts +0 -8
  329. package/src/legacy-runtime/runtime/ThreadListItemRuntime.ts +0 -10
  330. package/src/legacy-runtime/runtime/ThreadRuntime.ts +0 -14
  331. package/src/legacy-runtime/runtime-cores/adapters/RuntimeAdapterProvider.tsx +0 -7
  332. package/src/legacy-runtime/runtime-cores/adapters/attachment/CloudFileAttachmentAdapter.ts +0 -3
  333. package/src/legacy-runtime/runtime-cores/composer/BaseComposerRuntimeCore.ts +0 -1
  334. package/src/legacy-runtime/runtime-cores/composer/DefaultEditComposerRuntimeCore.ts +0 -1
  335. package/src/legacy-runtime/runtime-cores/composer/DefaultThreadComposerRuntimeCore.ts +0 -1
  336. package/src/legacy-runtime/runtime-cores/core/AssistantRuntimeCore.ts +0 -1
  337. package/src/legacy-runtime/runtime-cores/core/BaseAssistantRuntimeCore.ts +0 -1
  338. package/src/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.ts +0 -1
  339. package/src/legacy-runtime/runtime-cores/core/ComposerRuntimeCore.ts +0 -6
  340. package/src/legacy-runtime/runtime-cores/core/ThreadListRuntimeCore.ts +0 -4
  341. package/src/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.ts +0 -13
  342. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreAdapter.ts +0 -6
  343. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreRuntimeCore.ts +0 -1
  344. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadListRuntimeCore.ts +0 -2
  345. package/src/legacy-runtime/runtime-cores/external-store/ExternalStoreThreadRuntimeCore.ts +0 -4
  346. package/src/legacy-runtime/runtime-cores/external-store/ThreadMessageConverter.ts +0 -2
  347. package/src/legacy-runtime/runtime-cores/external-store/ThreadMessageLike.ts +0 -2
  348. package/src/legacy-runtime/runtime-cores/external-store/auto-status.ts +0 -1
  349. package/src/legacy-runtime/runtime-cores/external-store/createMessageConverter.ts +0 -2
  350. package/src/legacy-runtime/runtime-cores/external-store/external-message-converter.ts +0 -7
  351. package/src/legacy-runtime/runtime-cores/external-store/useExternalStoreRuntime.ts +0 -3
  352. package/src/legacy-runtime/runtime-cores/local/ChatModelAdapter.ts +0 -7
  353. package/src/legacy-runtime/runtime-cores/local/LocalRuntimeCore.ts +0 -1
  354. package/src/legacy-runtime/runtime-cores/local/LocalRuntimeOptions.ts +0 -3
  355. package/src/legacy-runtime/runtime-cores/local/LocalThreadListRuntimeCore.ts +0 -2
  356. package/src/legacy-runtime/runtime-cores/local/LocalThreadRuntimeCore.ts +0 -1
  357. package/src/legacy-runtime/runtime-cores/local/shouldContinue.ts +0 -1
  358. package/src/legacy-runtime/runtime-cores/local/useLocalRuntime.ts +0 -3
  359. package/src/legacy-runtime/runtime-cores/remote-thread-list/BaseSubscribable.ts +0 -1
  360. package/src/legacy-runtime/runtime-cores/remote-thread-list/EMPTY_THREAD_CORE.ts +0 -1
  361. package/src/legacy-runtime/runtime-cores/remote-thread-list/OptimisticState.ts +0 -1
  362. package/src/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.tsx +0 -3
  363. package/src/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.tsx +0 -3
  364. package/src/legacy-runtime/runtime-cores/remote-thread-list/adapter/cloud.tsx +0 -3
  365. package/src/legacy-runtime/runtime-cores/remote-thread-list/adapter/in-memory.ts +0 -1
  366. package/src/legacy-runtime/runtime-cores/remote-thread-list/types.ts +0 -7
  367. package/src/legacy-runtime/runtime-cores/remote-thread-list/useRemoteThreadListRuntime.ts +0 -3
  368. package/src/legacy-runtime/runtime-cores/utils/MessageRepository.ts +0 -3
  369. package/src/model-context/frame/AssistantFrame.test.ts +0 -400
  370. package/src/utils/hooks/useOnResizeContent.ts +0 -15
@@ -1,126 +1,19 @@
1
1
  "use client";
2
2
 
3
- import { useComposedRefs } from "radix-ui/internal";
4
- import { useCallback, useRef, type RefCallback } from "react";
5
- import { useAui, useAuiEvent, useAuiState } from "@assistant-ui/store";
6
3
  import {
7
- isUserScrollUp,
8
- isViewportAtBottom,
9
- viewportOverflows,
10
- } from "@assistant-ui/store/client";
11
- import { useOnResizeContent } from "../../utils/hooks/useOnResizeContent";
4
+ useCallback,
5
+ useEffect,
6
+ useInsertionEffect,
7
+ useRef,
8
+ type RefCallback,
9
+ } from "react";
10
+ import { useAui, useAuiEvent, useAuiState } from "@assistant-ui/store";
12
11
  import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
13
12
  import { useManagedRef } from "../../utils/hooks/useManagedRef";
14
- import { writableStore } from "../../context/ReadonlyStore";
15
13
  import { useThreadViewportStore } from "../../context/react/ThreadViewportContext";
14
+ import { getThreadViewportAutoScroll } from "../../context/stores/ThreadViewport";
16
15
  import { useIsomorphicLayoutEffect } from "../../utils/useIsomorphicLayoutEffect";
17
16
 
18
- const SCROLL_KEYS = new Set([
19
- " ",
20
- "ArrowUp",
21
- "ArrowDown",
22
- "PageUp",
23
- "PageDown",
24
- "Home",
25
- "End",
26
- ]);
27
-
28
- // Enter and Space activate focused controls, while navigation keys can
29
- // interrupt scrolling without producing a scroll event.
30
- const INTENT_CANCEL_KEYS = new Set(["Enter", ...SCROLL_KEYS]);
31
-
32
- // A control that consumes the activation key itself instead of acting on thread
33
- // content. `contenteditable="false"` marks a non-editable island inside an
34
- // editable tree, so it is excluded the way ComposerRoot already excludes it;
35
- // the input types left out are the ones a key activates rather than fills.
36
- const TEXT_ENTRY_SELECTOR = [
37
- "textarea",
38
- "select",
39
- "[contenteditable]:not([contenteditable='false'])",
40
- "input:not([type='checkbox']):not([type='radio']):not([type='button'])" +
41
- ":not([type='submit']):not([type='reset']):not([type='image'])" +
42
- ":not([type='range']):not([type='file']):not([type='color'])",
43
- ].join(", ");
44
-
45
- const MESSAGE_SELECTOR = "[data-message-id]";
46
-
47
- const DISCLOSURE_SELECTOR = "[aria-expanded], summary";
48
-
49
- // A popup trigger (menu, dialog, listbox) also carries aria-expanded, but what
50
- // it opens is portaled away from the thread, so only disclosures count.
51
- const isOpeningDisclosure = (target: Element): boolean => {
52
- const control = target.closest(DISCLOSURE_SELECTOR);
53
- if (!control) return false;
54
- if (control.tagName === "SUMMARY")
55
- return control.parentElement?.hasAttribute("open") === false;
56
- return (
57
- control.getAttribute("aria-expanded") === "false" &&
58
- (control.getAttribute("aria-haspopup") ?? "false") === "false" &&
59
- control.getAttribute("role") !== "combobox"
60
- );
61
- };
62
-
63
- type MessageOffset = { readonly id: string; readonly offset: number };
64
-
65
- const offsetInViewport = (element: Element, viewport: Element) =>
66
- element.getBoundingClientRect().top - viewport.getBoundingClientRect().top;
67
-
68
- const measureFirstMessage = (viewport: HTMLElement): MessageOffset | null => {
69
- const element = viewport.querySelector<HTMLElement>(MESSAGE_SELECTOR);
70
- const id = element?.dataset["messageId"];
71
- return element && id
72
- ? { id, offset: offsetInViewport(element, viewport) }
73
- : null;
74
- };
75
-
76
- /** The first message that starts in view, else the one spanning its top edge. */
77
- const measureReaderMessage = (viewport: HTMLElement): MessageOffset | null => {
78
- const { top, bottom } = viewport.getBoundingClientRect();
79
- let spanning: MessageOffset | null = null;
80
- for (const element of viewport.querySelectorAll<HTMLElement>(
81
- MESSAGE_SELECTOR,
82
- )) {
83
- const rect = element.getBoundingClientRect();
84
- if (rect.bottom <= top) continue;
85
- if (rect.top >= bottom) break;
86
- const id = element.dataset["messageId"];
87
- if (!id) continue;
88
- if (rect.top >= top) return { id, offset: rect.top - top };
89
- spanning ??= { id, offset: rect.top - top };
90
- }
91
- return spanning;
92
- };
93
-
94
- const findMessage = (viewport: HTMLElement, id: string) => {
95
- for (const element of viewport.querySelectorAll<HTMLElement>(
96
- MESSAGE_SELECTOR,
97
- )) {
98
- if (element.dataset["messageId"] === id) return element;
99
- }
100
- return null;
101
- };
102
-
103
- /**
104
- * Scrolls so the message sits at `anchor.offset` again, recording the
105
- * position it scrolls to in `heldScrollTop` before the scroll event can
106
- * report it; false when the message is gone.
107
- */
108
- const keepMessageAt = (
109
- viewport: HTMLElement,
110
- anchor: MessageOffset,
111
- heldScrollTop: { current: number },
112
- ) => {
113
- const element = findMessage(viewport, anchor.id);
114
- if (!element) return false;
115
- const delta = offsetInViewport(element, viewport) - anchor.offset;
116
- heldScrollTop.current = viewport.scrollTop + delta;
117
- if (Math.abs(delta) >= 1) {
118
- viewport.scrollTo({ top: heldScrollTop.current, behavior: "instant" });
119
- }
120
- heldScrollTop.current = viewport.scrollTop;
121
- return true;
122
- };
123
-
124
17
  export namespace useThreadViewportAutoScroll {
125
18
  export type Options = {
126
19
  /**
@@ -164,337 +57,39 @@ export const useThreadViewportAutoScroll = <TElement extends HTMLElement>({
164
57
  const divRef = useRef<TElement>(null);
165
58
  const hasMessages = useAuiState((s) => s.thread.messages.length > 0);
166
59
  const firstMessageId = useAuiState((s) => s.thread.messages[0]?.id);
167
- const threadId = useAuiState((s) => s.threadListItem.id);
60
+ const threadId = useAuiState((s) => s.optional.threadListItem?.id);
168
61
  const isRunning = useAuiState((s) => s.thread.isRunning);
169
- const initializeScrollRequestedRef = useRef(false);
170
- const scheduledFrameRef = useRef<number | null>(null);
171
-
172
62
  const threadViewportStore = useThreadViewportStore();
173
- if (autoScroll === undefined) {
174
- autoScroll = threadViewportStore.getState().turnAnchor !== "top";
175
- }
176
-
177
- const lastScrollTop = useRef<number>(0);
178
- const lastScrollHeight = useRef<number>(0);
179
- const lastObservedScrollHeight = useRef<number>(0);
180
- const lastObservedClientHeight = useRef<number>(0);
181
- const firstMessageRef = useRef<MessageOffset | null>(null);
182
- // The row the reader sees when earlier messages land, held until a gesture
183
- // or a scroll this hook did not make, so content above it that settles
184
- // afterwards (lazy layout, late media) does not move it on engines without
185
- // native scroll anchoring.
186
- const prependAnchorRef = useRef<MessageOffset | null>(null);
187
- const heldScrollTopRef = useRef(0);
188
-
189
- // Pending bottom-scroll intent. Planted by initialize/run-start/switch/button
190
- // triggers, cleared when handleScroll confirms we reached bottom, or when the
191
- // user actively scrolls up while content size is stable.
192
- const scrollingToBottomBehaviorRef = useRef<ScrollBehavior | null>(null);
193
- const followBottomRef = useRef(autoScroll);
194
- // Set by expanding a disclosure; suspends follow without clearing its intent
195
- // until a scroll gesture reaches an overflowing bottom (content that fits
196
- // reads as at the bottom), a run starts, the thread changes, or the reader
197
- // asks for the bottom again.
198
- const followPausedRef = useRef(false);
199
- const scrolledSincePauseRef = useRef(false);
200
- const previousAutoScrollRef = useRef(autoScroll);
201
-
202
- useIsomorphicLayoutEffect(() => {
203
- const previousAutoScroll = previousAutoScrollRef.current;
204
- previousAutoScrollRef.current = autoScroll;
205
- if (previousAutoScroll || !autoScroll) return;
206
-
207
- const div = divRef.current;
208
- followBottomRef.current = div !== null && isViewportAtBottom(div);
209
- }, [autoScroll]);
210
-
211
- const scrollToBottom = useCallback((behavior: ScrollBehavior) => {
212
- const div = divRef.current;
213
- if (!div) return;
214
-
215
- followBottomRef.current = true;
216
- followPausedRef.current = false;
217
- scrollingToBottomBehaviorRef.current = behavior;
218
- prependAnchorRef.current = null;
219
- div.scrollTo({ top: div.scrollHeight, behavior });
220
- lastScrollTop.current = div.scrollTop;
221
- lastScrollHeight.current = div.scrollHeight;
222
- }, []);
223
-
224
- const cancelScheduledFrame = useCallback(() => {
225
- if (scheduledFrameRef.current === null) return;
226
- cancelAnimationFrame(scheduledFrameRef.current);
227
- scheduledFrameRef.current = null;
228
- }, []);
229
-
230
- const scheduleScrollToBottom = useCallback(
231
- (behavior: ScrollBehavior) => {
232
- scrollingToBottomBehaviorRef.current = behavior;
233
- cancelScheduledFrame();
234
- scheduledFrameRef.current = requestAnimationFrame(() => {
235
- scheduledFrameRef.current = null;
236
- scrollToBottom(behavior);
237
- });
238
- },
239
- [cancelScheduledFrame, scrollToBottom],
240
- );
241
-
242
- useIsomorphicLayoutEffect(
243
- () => () => cancelScheduledFrame(),
244
- [cancelScheduledFrame],
245
- );
246
-
247
- const hasActiveTopAnchor = useCallback(() => {
248
- const state = threadViewportStore.getState();
249
- return (
250
- state.turnAnchor === "top" &&
251
- state.element.viewport === divRef.current &&
252
- state.element.anchor !== null
253
- );
254
- }, [threadViewportStore]);
255
-
256
- const onScroll = () => {
257
- const div = divRef.current;
258
- if (!div) return;
259
-
260
- if (
261
- prependAnchorRef.current &&
262
- Math.abs(div.scrollTop - heldScrollTopRef.current) >= 1
263
- ) {
264
- prependAnchorRef.current = null;
265
- }
266
-
267
- const isAtBottom = threadViewportStore.getState().isAtBottom;
268
- const newIsAtBottom = isViewportAtBottom(div);
269
-
270
- const isInFlightDownwardScroll =
271
- scrollingToBottomBehaviorRef.current !== null &&
272
- !newIsAtBottom &&
273
- lastScrollTop.current < div.scrollTop;
274
- if (isInFlightDownwardScroll) {
275
- // no-op: a smooth scroll-to-bottom fires many midpoint scroll events
276
- // before landing, don't flicker isAtBottom or clear intent mid-animation
277
- } else {
278
- const userScrolledUp = isUserScrollUp(
279
- {
280
- scrollTop: lastScrollTop.current,
281
- scrollHeight: lastScrollHeight.current,
282
- },
283
- div,
284
- );
285
-
286
- if (newIsAtBottom) {
287
- // newIsAtBottom is ambiguous when the viewport doesn't overflow —
288
- // keep intent alive until content can actually scroll
289
- if (viewportOverflows(div)) {
290
- scrollingToBottomBehaviorRef.current = null;
291
- if (scrolledSincePauseRef.current) followPausedRef.current = false;
292
- }
293
- if (autoScroll) followBottomRef.current = true;
294
- } else if (userScrolledUp) {
295
- cancelScheduledFrame();
296
- scrollingToBottomBehaviorRef.current = null;
297
- followBottomRef.current = false;
298
- }
299
-
300
- const shouldUpdate =
301
- newIsAtBottom || scrollingToBottomBehaviorRef.current === null;
302
-
303
- if (shouldUpdate && newIsAtBottom !== isAtBottom) {
304
- writableStore(threadViewportStore).setState({
305
- isAtBottom: newIsAtBottom,
306
- });
307
- }
308
- }
309
-
310
- lastScrollTop.current = div.scrollTop;
311
- lastScrollHeight.current = div.scrollHeight;
312
- firstMessageRef.current = measureFirstMessage(div);
313
- };
314
- const onScrollRef = useRef(onScroll);
315
- useIsomorphicLayoutEffect(() => {
316
- onScrollRef.current = onScroll;
317
- });
318
- const handleScroll = useCallback(() => onScrollRef.current(), []);
319
-
320
- const onResize = () => {
321
- const div = divRef.current;
322
- if (!div) return;
323
-
324
- const { scrollHeight, clientHeight } = div;
325
- if (
326
- scrollHeight === lastObservedScrollHeight.current &&
327
- clientHeight === lastObservedClientHeight.current
328
- ) {
329
- return;
330
- }
331
- lastObservedScrollHeight.current = scrollHeight;
332
- lastObservedClientHeight.current = clientHeight;
333
-
334
- // A scheduled frame owns the pending behavior; resize callbacks must not apply it early.
335
- if (scheduledFrameRef.current === null) {
336
- const scrollBehavior = scrollingToBottomBehaviorRef.current;
337
- if (scrollBehavior && hasActiveTopAnchor()) {
338
- // Let the top-anchor reserve own scrolling while a run starts to avoid a bottom-scroll race.
339
- scrollingToBottomBehaviorRef.current = null;
340
- } else if (scrollBehavior) {
341
- scrollToBottom(scrollBehavior);
342
- } else if (
343
- autoScroll &&
344
- !(isRunning && hasActiveTopAnchor()) &&
345
- followBottomRef.current &&
346
- !followPausedRef.current
347
- ) {
348
- scrollToBottom("instant");
349
- } else if (
350
- prependAnchorRef.current &&
351
- !keepMessageAt(div, prependAnchorRef.current, heldScrollTopRef)
352
- ) {
353
- prependAnchorRef.current = null;
354
- }
355
- }
356
-
357
- handleScroll();
358
- };
359
- const onResizeRef = useRef(onResize);
360
- useIsomorphicLayoutEffect(() => {
361
- onResizeRef.current = onResize;
63
+ const binding = getThreadViewportAutoScroll(threadViewportStore);
64
+ const { controller } = binding;
65
+
66
+ useInsertionEffect(() => {
67
+ binding.getOptions = () => {
68
+ const state = threadViewportStore.getState();
69
+ return {
70
+ autoScroll: autoScroll ?? state.turnAnchor !== "top",
71
+ holdFollow:
72
+ isRunning &&
73
+ state.turnAnchor === "top" &&
74
+ state.element.viewport === divRef.current &&
75
+ state.element.anchor !== null,
76
+ scrollToBottomOnInitialize,
77
+ scrollToBottomOnRunStart:
78
+ scrollToBottomOnRunStart && state.turnAnchor !== "top",
79
+ scrollToBottomOnThreadSwitch,
80
+ };
81
+ };
362
82
  });
363
- const resizeRef = useOnResizeContent(() => onResizeRef.current());
364
83
 
365
- const scrollRef = useManagedRef<HTMLElement>(
366
- useCallback(
367
- (el) => {
368
- // A user gesture invalidates pending bottom-scroll intent; otherwise an
369
- // intent kept alive by a non-overflowing thread (see handleScroll) hijacks
370
- // the next content growth, e.g. expanding a collapsible tool call. Keyboard
371
- // activation reaches that same content without ever emitting a pointer
372
- // event, so it has to cancel the intent too.
373
- const cancelPendingScrollToBottom = () => {
374
- // With nothing pending, the last scroll event already published isAtBottom.
375
- if (
376
- scrollingToBottomBehaviorRef.current === null &&
377
- scheduledFrameRef.current === null
378
- )
379
- return;
380
- // A scheduled frame re-plants the intent when it runs, so clearing the
381
- // ref alone leaves the gesture undone.
382
- cancelScheduledFrame();
383
- scrollingToBottomBehaviorRef.current = null;
384
- handleScroll();
385
- };
386
- // The composer renders inside the viewport, so its keystrokes bubble here;
387
- // cancellation keys only represent a gesture on thread content when they
388
- // originate outside text entry.
389
- const cancelOnKeyDown = (event: KeyboardEvent) => {
390
- if (!INTENT_CANCEL_KEYS.has(event.key)) return;
391
- const target = event.target as Element | null;
392
- if (target?.closest?.(TEXT_ENTRY_SELECTOR)) return;
393
- cancelPendingScrollToBottom();
394
- };
395
- // Expanding a disclosure inside a message, such as a reasoning block or a
396
- // tool call card, means the reader is inspecting it, so bottom follow
397
- // pauses until the reader scrolls or asks for the bottom again. Every
398
- // other interaction, including copying or selecting text, keeps
399
- // following. Activation is read from click, which keyboard activation also
400
- // dispatches, in the capture phase so the state is read before any handler
401
- // on the disclosure flips it or stops the event.
402
- const pauseFollowOnExpand = (event: MouseEvent) => {
403
- const target = event.target as Element | null;
404
- if (
405
- !target?.closest?.(MESSAGE_SELECTOR) ||
406
- target.closest(TEXT_ENTRY_SELECTOR) ||
407
- !isOpeningDisclosure(target)
408
- )
409
- return;
410
- followPausedRef.current = true;
411
- scrolledSincePauseRef.current = false;
412
- cancelPendingScrollToBottom();
413
- };
414
- const noteScrollGesture = (event: Event) => {
415
- if (event instanceof KeyboardEvent) {
416
- if (!SCROLL_KEYS.has(event.key)) return;
417
- if (
418
- (event.target as Element | null)?.closest?.(TEXT_ENTRY_SELECTOR)
419
- )
420
- return;
421
- } else if (event.type === "pointerdown" && event.target !== el) {
422
- return;
423
- }
424
- scrolledSincePauseRef.current = true;
425
- };
426
- const releasePrependAnchor = () => {
427
- prependAnchorRef.current = null;
428
- };
429
- const gestures = [
430
- "pointerdown",
431
- "wheel",
432
- "touchstart",
433
- "keydown",
434
- ] as const;
435
- const resumeGestures = [
436
- "pointerdown",
437
- "wheel",
438
- "touchmove",
439
- "keydown",
440
- ] as const;
441
- el.addEventListener("scroll", handleScroll);
442
- el.addEventListener("pointerdown", cancelPendingScrollToBottom);
443
- el.addEventListener("wheel", cancelPendingScrollToBottom, {
444
- passive: true,
445
- });
446
- el.addEventListener("touchstart", cancelPendingScrollToBottom, {
447
- passive: true,
448
- });
449
- el.addEventListener("keydown", cancelOnKeyDown);
450
- el.addEventListener("click", pauseFollowOnExpand, { capture: true });
451
- for (const gesture of resumeGestures) {
452
- el.addEventListener(gesture, noteScrollGesture, { passive: true });
453
- }
454
- for (const gesture of gestures) {
455
- el.addEventListener(gesture, releasePrependAnchor, { passive: true });
456
- }
457
- return () => {
458
- el.removeEventListener("scroll", handleScroll);
459
- el.removeEventListener("pointerdown", cancelPendingScrollToBottom);
460
- el.removeEventListener("wheel", cancelPendingScrollToBottom);
461
- el.removeEventListener("touchstart", cancelPendingScrollToBottom);
462
- el.removeEventListener("keydown", cancelOnKeyDown);
463
- el.removeEventListener("click", pauseFollowOnExpand, {
464
- capture: true,
465
- });
466
- for (const gesture of resumeGestures) {
467
- el.removeEventListener(gesture, noteScrollGesture);
468
- }
469
- for (const gesture of gestures) {
470
- el.removeEventListener(gesture, releasePrependAnchor);
471
- }
472
- };
473
- },
474
- [cancelScheduledFrame, handleScroll],
475
- ),
476
- );
477
-
478
- // Earlier messages prepended above the reader put the old first message back
479
- // where the reader saw it, so growth below it in the same commit (a
480
- // streaming reply) is not mistaken for the page; where the browser already
481
- // anchored the scroll, this is a no-op.
482
84
  const previousRef = useRef({ threadId, firstMessageId });
483
85
  useIsomorphicLayoutEffect(() => {
484
86
  const previous = previousRef.current;
485
87
  previousRef.current = { threadId, firstMessageId };
486
- if (previous.threadId !== threadId) {
487
- prependAnchorRef.current = null;
488
- return;
489
- }
490
- const div = divRef.current;
88
+ if (previous.threadId !== threadId) return;
491
89
  const previousFirstMessageId = previous.firstMessageId;
492
90
  if (
493
- !div ||
494
91
  previousFirstMessageId === undefined ||
495
- previousFirstMessageId === firstMessageId ||
496
- (autoScroll && followBottomRef.current && !followPausedRef.current) ||
497
- scrollingToBottomBehaviorRef.current !== null
92
+ previousFirstMessageId === firstMessageId
498
93
  )
499
94
  return;
500
95
  const prepended = aui.thread
@@ -502,60 +97,33 @@ export const useThreadViewportAutoScroll = <TElement extends HTMLElement>({
502
97
  .messages.some(
503
98
  (message, index) => index > 0 && message.id === previousFirstMessageId,
504
99
  );
505
- if (!prepended) return;
506
-
507
- const anchor = firstMessageRef.current;
508
- if (
509
- anchor?.id === previousFirstMessageId &&
510
- keepMessageAt(div, anchor, heldScrollTopRef)
511
- ) {
512
- prependAnchorRef.current = measureReaderMessage(div) ?? anchor;
513
- } else {
514
- // Message roots that render no `data-message-id` leave only the total
515
- // growth to go on, which counts any growth below the reader too.
516
- const top =
517
- div.scrollHeight - (lastScrollHeight.current - lastScrollTop.current);
518
- if (Math.abs(div.scrollTop - top) >= 1) {
519
- div.scrollTo({ top, behavior: "instant" });
520
- }
521
- }
522
- lastScrollTop.current = div.scrollTop;
523
- lastScrollHeight.current = div.scrollHeight;
524
- }, [aui, autoScroll, firstMessageId, threadId]);
100
+ if (prepended) controller.messagesPrepended(previousFirstMessageId);
101
+ }, [aui, controller, firstMessageId, threadId]);
525
102
 
526
103
  useIsomorphicLayoutEffect(() => {
527
- if (!scrollToBottomOnInitialize) return;
528
- if (!hasMessages) {
529
- initializeScrollRequestedRef.current = false;
530
- return;
531
- }
532
- if (initializeScrollRequestedRef.current) return;
533
-
534
- initializeScrollRequestedRef.current = true;
535
- // defer to an in-flight run (e.g. first message on a new thread) that
536
- // already planted intent — otherwise we'd downgrade its "auto" to "instant"
537
- if (scrollingToBottomBehaviorRef.current !== null) return;
538
- scheduleScrollToBottom("instant");
539
- }, [hasMessages, scheduleScrollToBottom, scrollToBottomOnInitialize]);
540
-
541
- useOnScrollToBottom(({ behavior }) => {
542
- scrollToBottom(behavior);
543
- });
544
-
545
- useAuiEvent("thread.runStart", () => {
546
- prependAnchorRef.current = null;
547
- followPausedRef.current = false;
548
- if (!scrollToBottomOnRunStart) return;
549
- if (threadViewportStore.getState().turnAnchor === "top") return;
550
- scheduleScrollToBottom("auto");
551
- });
552
-
553
- useAuiEvent("threads.selectionChanged", () => {
554
- followPausedRef.current = false;
555
- if (!scrollToBottomOnThreadSwitch) return;
556
- scheduleScrollToBottom("instant");
557
- });
104
+ controller.setHasMessages(hasMessages);
105
+ }, [controller, hasMessages, scrollToBottomOnInitialize]);
106
+
107
+ useOnScrollToBottom(({ behavior }) => controller.scrollToBottom(behavior));
108
+ useAuiEvent("thread.runStart", () => controller.runStarted());
109
+ useEffect(() => {
110
+ if (!aui.optional.threads) return;
111
+ return aui.on("threads.selectionChanged", () =>
112
+ controller.threadSwitched(),
113
+ );
114
+ }, [aui, controller]);
558
115
 
559
- const autoScrollRef = useComposedRefs<TElement>(resizeRef, scrollRef, divRef);
560
- return autoScrollRef as RefCallback<TElement>;
116
+ return useManagedRef(
117
+ useCallback(
118
+ (element: TElement) => {
119
+ divRef.current = element;
120
+ const detach = controller.attach(element);
121
+ return () => {
122
+ detach();
123
+ divRef.current = null;
124
+ };
125
+ },
126
+ [controller],
127
+ ),
128
+ );
561
129
  };
@@ -0,0 +1,52 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { cleanup, fireEvent, render } from "@testing-library/react";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import {
6
+ AssistantRuntimeProvider,
7
+ ThreadListItemByIndexProvider,
8
+ } from "@assistant-ui/core/react";
9
+ import {
10
+ AssistantRuntimeImpl,
11
+ ExternalStoreRuntimeCore,
12
+ } from "@assistant-ui/core/internal";
13
+ import { ThreadListItemPrimitiveUnarchive } from "./ThreadListItemUnarchive";
14
+
15
+ afterEach(cleanup);
16
+
17
+ describe("ThreadListItemPrimitive.Unarchive", () => {
18
+ it.each([true, false])(
19
+ "renders an actionable button only when unarchive is supported (%s)",
20
+ (supported) => {
21
+ const onUnarchive = vi.fn();
22
+ const runtime = new AssistantRuntimeImpl(
23
+ new ExternalStoreRuntimeCore({
24
+ messages: [],
25
+ onNew: async () => {},
26
+ adapters: {
27
+ threadList: {
28
+ archivedThreads: [{ id: "archived", status: "archived" }],
29
+ onUnarchive: supported ? onUnarchive : undefined,
30
+ },
31
+ },
32
+ }),
33
+ );
34
+ const { getByRole } = render(
35
+ <AssistantRuntimeProvider runtime={runtime}>
36
+ <ThreadListItemByIndexProvider index={0} archived>
37
+ <ThreadListItemPrimitiveUnarchive disabled={false}>
38
+ Restore
39
+ </ThreadListItemPrimitiveUnarchive>
40
+ </ThreadListItemByIndexProvider>
41
+ </AssistantRuntimeProvider>,
42
+ );
43
+ const button = getByRole("button", {
44
+ name: "Restore",
45
+ }) as HTMLButtonElement;
46
+ expect(button.disabled).toBe(!supported);
47
+ fireEvent.click(button);
48
+ if (supported) expect(onUnarchive).toHaveBeenCalledWith("archived");
49
+ else expect(onUnarchive).not.toHaveBeenCalled();
50
+ },
51
+ );
52
+ });
@@ -8,8 +8,8 @@ import {
8
8
  import { useThreadListItemUnarchive as useThreadListItemUnarchiveBehavior } from "@assistant-ui/core/react";
9
9
 
10
10
  const useThreadListItemUnarchive = () => {
11
- const { unarchive } = useThreadListItemUnarchiveBehavior();
12
- return unarchive;
11
+ const { unarchive, disabled } = useThreadListItemUnarchiveBehavior();
12
+ return disabled ? null : unarchive;
13
13
  };
14
14
 
15
15
  export namespace ThreadListItemPrimitiveUnarchive {
@@ -15,7 +15,7 @@ import { useAui } from "@assistant-ui/store";
15
15
  import { AssistantRuntimeProvider } from "../context";
16
16
  import * as MessagePrimitive from "../primitives/message";
17
17
  import * as ThreadPrimitive from "../primitives/thread";
18
- import { useLocalRuntime } from "../legacy-runtime/runtime-cores/local/useLocalRuntime";
18
+ import { useLocalRuntime } from "@assistant-ui/core/react";
19
19
  import {
20
20
  useAssistantDataUI,
21
21
  type ChatModelAdapter,
@@ -7,7 +7,7 @@ import { AssistantRuntimeProvider } from "../context";
7
7
  import * as MessagePartPrimitive from "../primitives/messagePart";
8
8
  import * as MessagePrimitive from "../primitives/message";
9
9
  import * as ThreadPrimitive from "../primitives/thread";
10
- import { useLocalRuntime } from "../legacy-runtime/runtime-cores/local/useLocalRuntime";
10
+ import { useLocalRuntime } from "@assistant-ui/core/react";
11
11
  import type { ChatModelAdapter, ThreadMessageLike } from "../index";
12
12
 
13
13
  const noOpAdapter: ChatModelAdapter = {
@@ -8,9 +8,9 @@ import {
8
8
  useRemoteThreadListRuntime,
9
9
  useRuntimeAdapters,
10
10
  type RuntimeAdapters as RuntimeAdaptersShape,
11
+ useLocalRuntime,
11
12
  } from "@assistant-ui/core/react";
12
13
  import { makeAdapter } from "./remote-thread-list-test-helpers";
13
- import { useLocalRuntime } from "../legacy-runtime/runtime-cores/local/useLocalRuntime";
14
14
  import { AssistantRuntimeProvider } from "../context";
15
15
  import type { ChatModelAdapter, RemoteThreadListAdapter } from "../index";
16
16
  import type { ThreadHistoryAdapter } from "@assistant-ui/core";