@assistant-ui/core 0.3.16 → 0.3.17

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 (305) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +2 -1
  5. package/dist/internal.js +2 -1
  6. package/dist/model-context/frame/provider.d.ts +3 -0
  7. package/dist/model-context/frame/provider.d.ts.map +1 -1
  8. package/dist/model-context/frame/provider.js +71 -21
  9. package/dist/model-context/frame/provider.js.map +1 -1
  10. package/dist/model-context/tool-call-text.d.ts +26 -0
  11. package/dist/model-context/tool-call-text.d.ts.map +1 -0
  12. package/dist/model-context/tool-call-text.js +18 -0
  13. package/dist/model-context/tool-call-text.js.map +1 -0
  14. package/dist/react/RuntimeAdapter.js +1 -1
  15. package/dist/react/adapters/LocalStorageThreadListAdapter.d.ts.map +1 -1
  16. package/dist/react/adapters/LocalStorageThreadListAdapter.js +1 -1
  17. package/dist/react/adapters/LocalStorageThreadListAdapter.js.map +1 -1
  18. package/dist/react/client/AdaptedRemoteThread.js +1 -1
  19. package/dist/react/client/InMemoryThreadList.js +1 -1
  20. package/dist/react/client/Interactables.js +1 -1
  21. package/dist/react/client/RemoteThreadList.d.ts +15 -0
  22. package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
  23. package/dist/react/client/RemoteThreadList.js +240 -138
  24. package/dist/react/client/RemoteThreadList.js.map +1 -1
  25. package/dist/react/client/Tools.d.ts.map +1 -1
  26. package/dist/react/client/Tools.js +6 -2
  27. package/dist/react/client/Tools.js.map +1 -1
  28. package/dist/react/index.d.ts +2 -1
  29. package/dist/react/index.js +2 -1
  30. package/dist/react/interactables-legacy/Interactables.js +4 -4
  31. package/dist/react/interactables-legacy/Interactables.js.map +1 -1
  32. package/dist/react/model-context/toolbox.d.ts +2 -14
  33. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  34. package/dist/react/model-context/toolbox.js +1 -13
  35. package/dist/react/model-context/toolbox.js.map +1 -1
  36. package/dist/react/model-context/useAssistantContext.js +37 -7
  37. package/dist/react/model-context/useAssistantContext.js.map +1 -1
  38. package/dist/react/model-context/useAuiToolOverrides.js +31 -5
  39. package/dist/react/model-context/useAuiToolOverrides.js.map +1 -1
  40. package/dist/react/model-context/useInlineRender.d.ts.map +1 -1
  41. package/dist/react/model-context/useInlineRender.js +10 -36
  42. package/dist/react/model-context/useInlineRender.js.map +1 -1
  43. package/dist/react/primitive-hooks/useMessageError.d.ts.map +1 -1
  44. package/dist/react/primitive-hooks/useMessageError.js +2 -8
  45. package/dist/react/primitive-hooks/useMessageError.js.map +1 -1
  46. package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -1
  47. package/dist/react/primitive-hooks/useThreadListLoadMore.js +2 -4
  48. package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -1
  49. package/dist/react/primitives/composer/ComposerAttachments.d.ts.map +1 -1
  50. package/dist/react/primitives/composer/ComposerAttachments.js +4 -3
  51. package/dist/react/primitives/composer/ComposerAttachments.js.map +1 -1
  52. package/dist/react/primitives/composer/ComposerQueue.d.ts.map +1 -1
  53. package/dist/react/primitives/composer/ComposerQueue.js +4 -3
  54. package/dist/react/primitives/composer/ComposerQueue.js.map +1 -1
  55. package/dist/react/primitives/message/MessageAttachments.d.ts.map +1 -1
  56. package/dist/react/primitives/message/MessageAttachments.js +7 -6
  57. package/dist/react/primitives/message/MessageAttachments.js.map +1 -1
  58. package/dist/react/primitives/message/MessageGroupedParts.js +2 -2
  59. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  60. package/dist/react/primitives/message/MessageParts.js +3 -3
  61. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  62. package/dist/react/primitives/threadList/ThreadListItems.d.ts.map +1 -1
  63. package/dist/react/primitives/threadList/ThreadListItems.js +5 -4
  64. package/dist/react/primitives/threadList/ThreadListItems.js.map +1 -1
  65. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +6 -4
  66. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
  67. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +44 -22
  68. package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
  69. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +6 -5
  70. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  71. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +79 -62
  72. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  73. package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -6
  74. package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
  75. package/dist/react/runtimes/RemoteThreadResource.js +3 -3
  76. package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
  77. package/dist/react/runtimes/assistant-transport/useLatestRef.js +2 -2
  78. package/dist/react/runtimes/assistant-transport/useLatestRef.js.map +1 -1
  79. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
  80. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +23 -18
  81. package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
  82. package/dist/react/runtimes/cloud/auiV0.d.ts +5 -1
  83. package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
  84. package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
  85. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts +29 -0
  86. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -0
  87. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +159 -0
  88. package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -0
  89. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts +1 -9
  90. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  91. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js +16 -153
  92. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  93. package/dist/react/runtimes/external-message-converter.js +1 -1
  94. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  95. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  96. package/dist/react/runtimes/useRemoteThreadListRuntime.js +14 -6
  97. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  98. package/dist/react/types/MessagePartComponentTypes.d.ts +7 -3
  99. package/dist/react/types/MessagePartComponentTypes.d.ts.map +1 -1
  100. package/dist/react/types/scopes/tools.d.ts +2 -0
  101. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  102. package/dist/runtime/api/message-part-runtime.d.ts +2 -2
  103. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  104. package/dist/runtime/api/message-part-runtime.js +1 -1
  105. package/dist/runtime/api/message-part-runtime.js.map +1 -1
  106. package/dist/runtime/api/thread-list-runtime.d.ts +9 -1
  107. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  108. package/dist/runtime/api/thread-list-runtime.js +5 -0
  109. package/dist/runtime/api/thread-list-runtime.js.map +1 -1
  110. package/dist/runtime/api/thread-runtime.d.ts +1 -1
  111. package/dist/runtime/base/base-thread-runtime-core.d.ts +2 -2
  112. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  113. package/dist/runtime/base/base-thread-runtime-core.js +2 -2
  114. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  115. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +14 -2
  116. package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
  117. package/dist/runtime/interfaces/thread-runtime-core.d.ts +15 -1
  118. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  119. package/dist/runtime/utils/auto-status.d.ts +1 -1
  120. package/dist/runtime/utils/auto-status.d.ts.map +1 -1
  121. package/dist/runtime/utils/auto-status.js +6 -2
  122. package/dist/runtime/utils/auto-status.js.map +1 -1
  123. package/dist/runtime/utils/external-message-conversion.d.ts +3 -2
  124. package/dist/runtime/utils/external-message-conversion.d.ts.map +1 -1
  125. package/dist/runtime/utils/external-message-conversion.js +4 -3
  126. package/dist/runtime/utils/external-message-conversion.js.map +1 -1
  127. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts +6 -3
  128. package/dist/runtime/utils/resolveToolApprovalResponse.d.ts.map +1 -1
  129. package/dist/runtime/utils/resolveToolApprovalResponse.js +12 -3
  130. package/dist/runtime/utils/resolveToolApprovalResponse.js.map +1 -1
  131. package/dist/runtime/utils/thread-message-like.d.ts +2 -10
  132. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  133. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  134. package/dist/runtime/utils/tool-call-tree.d.ts +39 -0
  135. package/dist/runtime/utils/tool-call-tree.d.ts.map +1 -0
  136. package/dist/runtime/utils/tool-call-tree.js +85 -0
  137. package/dist/runtime/utils/tool-call-tree.js.map +1 -0
  138. package/dist/runtimes/external-store/external-store-adapter.d.ts +29 -2
  139. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  140. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +2 -1
  141. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  142. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +34 -28
  143. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  144. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts +1 -1
  145. package/dist/runtimes/inert/InertThreadRuntimeCore.d.ts.map +1 -1
  146. package/dist/runtimes/inert/InertThreadRuntimeCore.js.map +1 -1
  147. package/dist/runtimes/local/local-thread-runtime-core.d.ts +1 -1
  148. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  149. package/dist/runtimes/local/local-thread-runtime-core.js +3 -1
  150. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  151. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +1 -1
  152. package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
  153. package/dist/runtimes/remote-thread-list/remote-thread-state.js +30 -13
  154. package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
  155. package/dist/runtimes/remote-thread-list/title.d.ts +9 -0
  156. package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -0
  157. package/dist/runtimes/remote-thread-list/title.js +6 -0
  158. package/dist/runtimes/remote-thread-list/title.js.map +1 -0
  159. package/dist/runtimes/remote-thread-list/types.d.ts +6 -5
  160. package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
  161. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +31 -8
  162. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -1
  163. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +97 -60
  164. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -1
  165. package/dist/store/clients/external-thread.d.ts +1 -1
  166. package/dist/store/clients/external-thread.d.ts.map +1 -1
  167. package/dist/store/clients/external-thread.js +11 -4
  168. package/dist/store/clients/external-thread.js.map +1 -1
  169. package/dist/store/clients/single-thread-list.js +1 -1
  170. package/dist/store/clients/suggestions.d.ts.map +1 -1
  171. package/dist/store/clients/suggestions.js +18 -6
  172. package/dist/store/clients/suggestions.js.map +1 -1
  173. package/dist/store/clients/thread-message-client.js +1 -1
  174. package/dist/store/clients/thread-selection-events.js +1 -1
  175. package/dist/store/index.d.ts +2 -1
  176. package/dist/store/index.js +2 -1
  177. package/dist/store/internal.d.ts +2 -2
  178. package/dist/store/internal.js +2 -2
  179. package/dist/store/primitive-predicates.d.ts +4 -1
  180. package/dist/store/primitive-predicates.d.ts.map +1 -1
  181. package/dist/store/primitive-predicates.js +9 -1
  182. package/dist/store/primitive-predicates.js.map +1 -1
  183. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  184. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  185. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  186. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  187. package/dist/store/runtime-clients/thread-list-runtime-client.js +124 -105
  188. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  189. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  190. package/dist/store/runtime-clients/useSubscribable.d.ts +3 -1
  191. package/dist/store/runtime-clients/useSubscribable.d.ts.map +1 -1
  192. package/dist/store/runtime-clients/useSubscribable.js +1 -1
  193. package/dist/store/runtime-clients/useSubscribable.js.map +1 -1
  194. package/dist/store/scopes/part.d.ts +6 -3
  195. package/dist/store/scopes/part.d.ts.map +1 -1
  196. package/dist/subscribable/subscribable.d.ts +8 -1
  197. package/dist/subscribable/subscribable.d.ts.map +1 -1
  198. package/dist/subscribable/subscribable.js +24 -16
  199. package/dist/subscribable/subscribable.js.map +1 -1
  200. package/dist/types/message.d.ts +40 -1
  201. package/dist/types/message.d.ts.map +1 -1
  202. package/dist/types/message.js +7 -1
  203. package/dist/types/message.js.map +1 -1
  204. package/package.json +12 -17
  205. package/src/index.ts +6 -1
  206. package/src/internal.ts +8 -0
  207. package/src/model-context/frame/provider.test.ts +217 -1
  208. package/src/model-context/frame/provider.ts +111 -23
  209. package/src/model-context/tool-call-text.test.ts +67 -0
  210. package/src/model-context/tool-call-text.ts +67 -0
  211. package/src/react/AssistantRuntimeProvider.test.tsx +66 -2
  212. package/src/react/adapters/LocalStorageThreadListAdapter.tsx +1 -3
  213. package/src/react/client/RemoteThreadList.background.test.ts +384 -0
  214. package/src/react/client/RemoteThreadList.concurrent.test.tsx +274 -0
  215. package/src/react/client/RemoteThreadList.test.ts +273 -1
  216. package/src/react/client/RemoteThreadList.ts +293 -83
  217. package/src/react/client/Tools.ts +7 -1
  218. package/src/react/index.ts +4 -0
  219. package/src/react/model-context/modelContextHooks.test.tsx +131 -0
  220. package/src/react/model-context/toolbox.test.ts +9 -9
  221. package/src/react/model-context/toolbox.ts +9 -36
  222. package/src/react/model-context/useAssistantContext.ts +4 -2
  223. package/src/react/model-context/useAuiToolOverrides.ts +4 -2
  224. package/src/react/model-context/useInlineRender.test.tsx +48 -0
  225. package/src/react/model-context/useInlineRender.ts +14 -10
  226. package/src/react/primitive-hooks/useMessageError.ts +2 -19
  227. package/src/react/primitive-hooks/useThreadListLoadMore.ts +2 -3
  228. package/src/react/primitives/composer/ComposerAttachments.test.tsx +76 -0
  229. package/src/react/primitives/composer/ComposerAttachments.tsx +9 -4
  230. package/src/react/primitives/composer/ComposerQueue.test.tsx +72 -0
  231. package/src/react/primitives/composer/ComposerQueue.tsx +7 -4
  232. package/src/react/primitives/message/MessageAttachments.test.tsx +107 -37
  233. package/src/react/primitives/message/MessageAttachments.tsx +10 -7
  234. package/src/react/primitives/message/MessageGroupedParts.tsx +2 -2
  235. package/src/react/primitives/message/MessageParts.tsx +3 -3
  236. package/src/react/primitives/threadList/ThreadListItems.test.tsx +99 -0
  237. package/src/react/primitives/threadList/ThreadListItems.tsx +8 -5
  238. package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +94 -1
  239. package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +72 -31
  240. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +169 -0
  241. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +114 -68
  242. package/src/react/runtimes/RemoteThreadResource.ts +1 -4
  243. package/src/react/runtimes/assistant-transport/transport-scheduling.test.ts +51 -2
  244. package/src/react/runtimes/assistant-transport/useLatestRef.ts +2 -2
  245. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.ts +45 -0
  246. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +65 -1
  247. package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +42 -35
  248. package/src/react/runtimes/cloud/auiV0.ts +5 -0
  249. package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +88 -0
  250. package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +236 -0
  251. package/src/react/runtimes/cloud/useCloudThreadListAdapter.test.tsx +90 -1
  252. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +33 -238
  253. package/src/react/runtimes/external-message-converter.ts +1 -0
  254. package/src/react/runtimes/useRemoteThreadListRuntime.concurrent.test.tsx +216 -0
  255. package/src/react/runtimes/useRemoteThreadListRuntime.ts +24 -6
  256. package/src/react/types/MessagePartComponentTypes.ts +7 -3
  257. package/src/react/types/scopes/tools.ts +2 -0
  258. package/src/runtime/api/message-part-runtime.ts +3 -3
  259. package/src/runtime/api/thread-list-runtime.ts +25 -1
  260. package/src/runtime/base/base-thread-runtime-core.test.ts +94 -3
  261. package/src/runtime/base/base-thread-runtime-core.ts +10 -4
  262. package/src/runtime/interfaces/thread-list-runtime-core.ts +20 -1
  263. package/src/runtime/interfaces/thread-runtime-core.ts +17 -1
  264. package/src/runtime/utils/auto-status.test.ts +57 -0
  265. package/src/runtime/utils/auto-status.ts +13 -1
  266. package/src/runtime/utils/external-message-conversion.test.ts +97 -0
  267. package/src/runtime/utils/external-message-conversion.ts +17 -1
  268. package/src/runtime/utils/resolveToolApprovalResponse.test.ts +73 -0
  269. package/src/runtime/utils/resolveToolApprovalResponse.ts +27 -6
  270. package/src/runtime/utils/thread-message-like.ts +2 -10
  271. package/src/runtime/utils/tool-call-tree.test.ts +191 -0
  272. package/src/runtime/utils/tool-call-tree.ts +97 -0
  273. package/src/runtimes/external-store/external-store-adapter.ts +38 -2
  274. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +56 -33
  275. package/src/runtimes/inert/InertThreadRuntimeCore.ts +1 -1
  276. package/src/runtimes/local/local-thread-runtime-core.test.ts +29 -3
  277. package/src/runtimes/local/local-thread-runtime-core.ts +5 -1
  278. package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +112 -0
  279. package/src/runtimes/remote-thread-list/remote-thread-state.ts +55 -16
  280. package/src/runtimes/remote-thread-list/title.ts +3 -0
  281. package/src/runtimes/remote-thread-list/types.ts +6 -5
  282. package/src/runtimes/tool-invocations/EDGE_CASES.md +110 -5
  283. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +750 -136
  284. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +144 -91
  285. package/src/store/clients/external-thread.ts +14 -8
  286. package/src/store/clients/suggestions.test.ts +155 -1
  287. package/src/store/clients/suggestions.ts +42 -34
  288. package/src/store/index.ts +4 -0
  289. package/src/store/internal.ts +2 -0
  290. package/src/store/primitive-predicates.ts +35 -0
  291. package/src/store/runtime-clients/thread-list-runtime-client.ts +16 -2
  292. package/src/store/runtime-clients/useSubscribable.ts +8 -2
  293. package/src/store/scopes/part.ts +6 -3
  294. package/src/subscribable/subscribable.test.ts +50 -0
  295. package/src/subscribable/subscribable.ts +33 -21
  296. package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +111 -0
  297. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +78 -0
  298. package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +52 -0
  299. package/src/tests/external-store-optimistic-id.test.ts +79 -0
  300. package/src/tests/external-store-repository-instance.test.ts +166 -0
  301. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +22 -16
  302. package/src/tests/external-thread-attachments.test.tsx +85 -1
  303. package/src/tests/remote-thread-list-adapter-switch.test.ts +66 -1
  304. package/src/tests/thread-switch-events.test.tsx +397 -2
  305. package/src/types/message.ts +50 -2
@@ -0,0 +1,131 @@
1
+ // @vitest-environment jsdom
2
+
3
+ import { act, cleanup, render, waitFor } from "@testing-library/react";
4
+ import { startTransition, Suspense } from "react";
5
+ import { afterEach, describe, expect, it, vi } from "vitest";
6
+
7
+ const { aui, getProvider, resetProvider } = vi.hoisted(() => {
8
+ type TestProvider = {
9
+ getModelContext: () => {
10
+ system?: string | undefined;
11
+ tools?:
12
+ | Record<string, { execute?: (() => unknown) | undefined }>
13
+ | undefined;
14
+ };
15
+ };
16
+ let provider: TestProvider | undefined;
17
+
18
+ return {
19
+ aui: {
20
+ modelContext: {
21
+ register: vi.fn((nextProvider: TestProvider) => {
22
+ provider = nextProvider;
23
+ return () => {
24
+ if (provider === nextProvider) provider = undefined;
25
+ };
26
+ }),
27
+ },
28
+ },
29
+ getProvider: () => provider,
30
+ resetProvider: () => {
31
+ provider = undefined;
32
+ },
33
+ };
34
+ });
35
+
36
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
37
+ ...(await importOriginal()),
38
+ useAui: () => aui,
39
+ }));
40
+
41
+ import { useAssistantContext } from "./useAssistantContext";
42
+ import { useAuiToolOverrides } from "./useAuiToolOverrides";
43
+
44
+ afterEach(() => {
45
+ cleanup();
46
+ resetProvider();
47
+ });
48
+
49
+ describe("model context hooks", () => {
50
+ it("keeps assistant context scoped to the committed render", async () => {
51
+ const pending = new Promise<never>(() => {});
52
+ const renderB = vi.fn();
53
+ const Probe = ({ value, suspend }: { value: string; suspend: boolean }) => {
54
+ useAssistantContext({ getContext: () => value });
55
+ if (value === "workspace-b") renderB();
56
+ if (suspend) throw pending;
57
+ return null;
58
+ };
59
+
60
+ const view = render(
61
+ <Suspense fallback={null}>
62
+ <Probe value="workspace-a" suspend={false} />
63
+ </Suspense>,
64
+ );
65
+ await waitFor(() => expect(getProvider()).toBeDefined());
66
+
67
+ act(() => {
68
+ startTransition(() => {
69
+ view.rerender(
70
+ <Suspense fallback={null}>
71
+ <Probe value="workspace-b" suspend />
72
+ </Suspense>,
73
+ );
74
+ });
75
+ });
76
+
77
+ expect(renderB).toHaveBeenCalled();
78
+ expect(getProvider()?.getModelContext().system).toBe("workspace-a");
79
+
80
+ view.rerender(
81
+ <Suspense fallback={null}>
82
+ <Probe value="workspace-b" suspend={false} />
83
+ </Suspense>,
84
+ );
85
+
86
+ expect(getProvider()?.getModelContext().system).toBe("workspace-b");
87
+ });
88
+
89
+ it("keeps tool overrides scoped to the committed render", async () => {
90
+ const pending = new Promise<never>(() => {});
91
+ const renderB = vi.fn();
92
+ const Probe = ({ value, suspend }: { value: string; suspend: boolean }) => {
93
+ useAuiToolOverrides({ search: { execute: () => value } });
94
+ if (value === "workspace-b") renderB();
95
+ if (suspend) throw pending;
96
+ return null;
97
+ };
98
+
99
+ const view = render(
100
+ <Suspense fallback={null}>
101
+ <Probe value="workspace-a" suspend={false} />
102
+ </Suspense>,
103
+ );
104
+ await waitFor(() => expect(getProvider()).toBeDefined());
105
+
106
+ act(() => {
107
+ startTransition(() => {
108
+ view.rerender(
109
+ <Suspense fallback={null}>
110
+ <Probe value="workspace-b" suspend />
111
+ </Suspense>,
112
+ );
113
+ });
114
+ });
115
+
116
+ expect(renderB).toHaveBeenCalled();
117
+ expect(getProvider()?.getModelContext().tools?.search?.execute?.()).toBe(
118
+ "workspace-a",
119
+ );
120
+
121
+ view.rerender(
122
+ <Suspense fallback={null}>
123
+ <Probe value="workspace-b" suspend={false} />
124
+ </Suspense>,
125
+ );
126
+
127
+ expect(getProvider()?.getModelContext().tools?.search?.execute?.()).toBe(
128
+ "workspace-b",
129
+ );
130
+ });
131
+ });
@@ -19,7 +19,7 @@ describe("makeToolCallTextComponent", () => {
19
19
  status: { type: "running" },
20
20
  addResult: () => {},
21
21
  resume: () => {},
22
- respondToApproval: () => {},
22
+ respondToApproval: async () => {},
23
23
  }),
24
24
  ).toBe("Searching...");
25
25
 
@@ -34,7 +34,7 @@ describe("makeToolCallTextComponent", () => {
34
34
  status: { type: "complete" },
35
35
  addResult: () => {},
36
36
  resume: () => {},
37
- respondToApproval: () => {},
37
+ respondToApproval: async () => {},
38
38
  }),
39
39
  ).toBe("Done searching");
40
40
  });
@@ -59,7 +59,7 @@ describe("makeToolCallTextComponent", () => {
59
59
  status: { type: "running" },
60
60
  addResult: () => {},
61
61
  resume: () => {},
62
- respondToApproval: () => {},
62
+ respondToApproval: async () => {},
63
63
  }),
64
64
  ).toBe("Searching docs...");
65
65
 
@@ -74,7 +74,7 @@ describe("makeToolCallTextComponent", () => {
74
74
  status: { type: "complete" },
75
75
  addResult: () => {},
76
76
  resume: () => {},
77
- respondToApproval: () => {},
77
+ respondToApproval: async () => {},
78
78
  }),
79
79
  ).toBe("Found 3 results for docs");
80
80
  });
@@ -94,7 +94,7 @@ describe("makeToolCallTextComponent", () => {
94
94
  argsText: "{}",
95
95
  addResult: () => {},
96
96
  resume: () => {},
97
- respondToApproval: () => {},
97
+ respondToApproval: async () => {},
98
98
  } as unknown as ToolCallMessagePartProps),
99
99
  ).toBe("Complete");
100
100
  });
@@ -115,7 +115,7 @@ describe("makeToolCallTextComponent", () => {
115
115
  status: { type: "incomplete", reason: "error" },
116
116
  addResult: () => {},
117
117
  resume: () => {},
118
- respondToApproval: () => {},
118
+ respondToApproval: async () => {},
119
119
  }),
120
120
  ).toBe("Finished");
121
121
  });
@@ -136,7 +136,7 @@ describe("makeToolCallTextComponent", () => {
136
136
  status: { type: "requires-action", reason: "interrupt" },
137
137
  addResult: () => {},
138
138
  resume: () => {},
139
- respondToApproval: () => {},
139
+ respondToApproval: async () => {},
140
140
  }),
141
141
  ).toBe("Waiting for approval...");
142
142
  });
@@ -156,7 +156,7 @@ describe("makeToolCallTextComponent", () => {
156
156
  status: { type: "complete" },
157
157
  addResult: () => {},
158
158
  resume: () => {},
159
- respondToApproval: () => {},
159
+ respondToApproval: async () => {},
160
160
  }),
161
161
  ).toBeNull();
162
162
  });
@@ -176,7 +176,7 @@ describe("makeToolCallTextComponent", () => {
176
176
  status: { type: "running" },
177
177
  addResult: () => {},
178
178
  resume: () => {},
179
- respondToApproval: () => {},
179
+ respondToApproval: async () => {},
180
180
  }),
181
181
  ).toBeNull();
182
182
  });
@@ -9,6 +9,15 @@ import type {
9
9
  ToolCallMessagePartComponent,
10
10
  ToolCallMessagePartProps,
11
11
  } from "../types/MessagePartComponentTypes";
12
+ import {
13
+ resolveToolCallText,
14
+ type ToolCallText as NeutralToolCallText,
15
+ } from "../../model-context/tool-call-text";
16
+
17
+ export type ToolCallText<
18
+ TArgs extends Record<string, unknown>,
19
+ TResult,
20
+ > = NeutralToolCallText<TArgs, TResult, ReactNode>;
12
21
 
13
22
  /**
14
23
  * Resolves whether a tool's UI should be presented standalone (outside the
@@ -61,42 +70,6 @@ type ToolStreamCall<TArgs extends Record<string, unknown>, TResult> = (
61
70
  context: ToolExecuteContext,
62
71
  ) => void;
63
72
 
64
- type ToolCallRunningText<TArgs extends Record<string, unknown>> =
65
- | ReactNode
66
- | ((options: { args: TArgs }) => ReactNode);
67
-
68
- type ToolCallCompleteText<TArgs extends Record<string, unknown>, TResult> =
69
- | ReactNode
70
- | ((options: { args: TArgs; result: TResult | undefined }) => ReactNode);
71
-
72
- export type ToolCallText<TArgs extends Record<string, unknown>, TResult> =
73
- | {
74
- running: ToolCallRunningText<TArgs>;
75
- complete?: ToolCallCompleteText<TArgs, TResult> | undefined;
76
- }
77
- | {
78
- running?: ToolCallRunningText<TArgs> | undefined;
79
- complete: ToolCallCompleteText<TArgs, TResult>;
80
- };
81
-
82
- const resolveToolCallText = <TArgs extends Record<string, unknown>, TResult>(
83
- text: ToolCallText<TArgs, TResult>,
84
- part: ToolCallMessagePartProps<TArgs, TResult>,
85
- ): ReactNode => {
86
- const isRunning =
87
- part.status?.type === "running" || part.status?.type === "requires-action";
88
-
89
- if (!isRunning) {
90
- const value = text.complete;
91
- if (typeof value !== "function") return value ?? null;
92
- return value({ args: part.args, result: part.result });
93
- }
94
-
95
- const value = text.running;
96
- if (typeof value !== "function") return value ?? null;
97
- return value({ args: part.args });
98
- };
99
-
100
73
  export const makeToolCallTextComponent = <
101
74
  TArgs extends Record<string, unknown>,
102
75
  TResult,
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useInsertionEffect, useRef } from "react";
2
2
  import { useAui } from "@assistant-ui/store";
3
3
  import type { AssistantContextConfig } from "../..";
4
4
 
@@ -8,7 +8,9 @@ export const useAssistantContext = (config: AssistantContextConfig) => {
8
8
  const { getContext, disabled = false } = config;
9
9
  const aui = useAui();
10
10
  const getContextRef = useRef(getContext);
11
- getContextRef.current = getContext;
11
+ useInsertionEffect(() => {
12
+ getContextRef.current = getContext;
13
+ }, [getContext]);
12
14
 
13
15
  useEffect(() => {
14
16
  if (disabled) return;
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useInsertionEffect, useRef } from "react";
2
2
  import { useAui } from "@assistant-ui/store";
3
3
  import type { Tool } from "assistant-stream";
4
4
 
@@ -25,7 +25,9 @@ type AuiToolOverrides = Record<string, AuiToolOverride<any, any>>;
25
25
  export function useAuiToolOverrides(overrides: AuiToolOverrides): void {
26
26
  const aui = useAui();
27
27
  const overridesRef = useRef(overrides);
28
- overridesRef.current = overrides;
28
+ useInsertionEffect(() => {
29
+ overridesRef.current = overrides;
30
+ }, [overrides]);
29
31
 
30
32
  useEffect(() => {
31
33
  return aui.modelContext.register({
@@ -0,0 +1,48 @@
1
+ // @vitest-environment jsdom
2
+ import { useState, type FC } from "react";
3
+ import { act, cleanup, render, screen } from "@testing-library/react";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { useInlineRender } from "./useInlineRender";
6
+ import type { ToolCallMessagePartProps } from "../types/MessagePartComponentTypes";
7
+
8
+ afterEach(() => cleanup());
9
+
10
+ type Props = ToolCallMessagePartProps<any, any>;
11
+
12
+ // The point of useInlineRender is a component whose identity survives a
13
+ // changing toolUI, so inner state must not be lost when toolUI swaps.
14
+ const makeToolUI = (label: string): FC<Props> =>
15
+ function Inner() {
16
+ const [n, setN] = useState(0);
17
+ return (
18
+ <button onClick={() => setN((v) => v + 1)}>
19
+ {label}:{n}
20
+ </button>
21
+ );
22
+ };
23
+
24
+ describe("useInlineRender", () => {
25
+ it("follows toolUI changes without remounting the inner tree", async () => {
26
+ let swap!: (fc: FC<Props>) => void;
27
+ const identities = new Set<unknown>();
28
+
29
+ const Host = () => {
30
+ const [toolUI, setToolUI] = useState(() => makeToolUI("a"));
31
+ swap = (fc) => setToolUI(() => fc);
32
+ const ToolUI = useInlineRender(toolUI);
33
+ identities.add(ToolUI);
34
+ return <ToolUI {...({} as Props)} />;
35
+ };
36
+
37
+ render(<Host />);
38
+ expect(screen.getByRole("button").textContent).toBe("a:0");
39
+
40
+ await act(async () => screen.getByRole("button").click());
41
+ expect(screen.getByRole("button").textContent).toBe("a:1");
42
+
43
+ await act(async () => swap(makeToolUI("b")));
44
+ // label follows the new toolUI, counter survives => no remount
45
+ expect(screen.getByRole("button").textContent).toBe("b:1");
46
+ expect(identities.size).toBe(1);
47
+ });
48
+ });
@@ -1,25 +1,29 @@
1
1
  import { type FC, useCallback, useEffect, useState } from "react";
2
2
  import type { ToolCallMessagePartProps } from "../types/MessagePartComponentTypes";
3
- import { create } from "zustand";
3
+ import { WritableSubscribable } from "../../subscribable/subscribable";
4
+ import { useSubscribable } from "../../store/runtime-clients/useSubscribable";
4
5
 
5
6
  export const useInlineRender = <TArgs, TResult>(
6
7
  toolUI: FC<ToolCallMessagePartProps<TArgs, TResult>>,
7
8
  ): FC<ToolCallMessagePartProps<TArgs, TResult>> => {
8
- const [useToolUIStore] = useState(() =>
9
- create(() => ({
10
- toolUI,
11
- })),
9
+ const [toolUIStore] = useState(
10
+ () =>
11
+ new WritableSubscribable<FC<ToolCallMessagePartProps<TArgs, TResult>>>(
12
+ toolUI,
13
+ ),
12
14
  );
13
15
 
14
16
  useEffect(() => {
15
- useToolUIStore.setState({ toolUI });
16
- }, [toolUI, useToolUIStore]);
17
+ toolUIStore.setState(toolUI);
18
+ }, [toolUI, toolUIStore]);
17
19
 
18
20
  return useCallback(
19
21
  function ToolUI(args) {
20
- const store = useToolUIStore();
21
- return store.toolUI(args);
22
+ // Invoked as a plain function so its hooks belong to this component,
23
+ // which keeps its identity while `toolUI` changes underneath.
24
+ const currentToolUI = useSubscribable(toolUIStore);
25
+ return currentToolUI(args);
22
26
  },
23
- [useToolUIStore],
27
+ [toolUIStore],
24
28
  );
25
29
  };
@@ -1,23 +1,6 @@
1
1
  import { useAuiState } from "@assistant-ui/store";
2
+ import { messageErrorText } from "../../store/primitive-predicates";
2
3
 
3
4
  export const useMessageError = () => {
4
- return useAuiState((s) => {
5
- if (
6
- s.message.status?.type !== "incomplete" ||
7
- s.message.status.reason !== "error"
8
- ) {
9
- return undefined;
10
- }
11
- const error = s.message.status.error;
12
- if (typeof error === "string") return error;
13
- if (
14
- typeof error === "object" &&
15
- error !== null &&
16
- "message" in error &&
17
- typeof error.message === "string"
18
- ) {
19
- return error.message;
20
- }
21
- return error ?? "An error occurred";
22
- });
5
+ return useAuiState(messageErrorText);
23
6
  };
@@ -1,11 +1,10 @@
1
1
  import { useCallback } from "react";
2
2
  import { useAui, useAuiState } from "@assistant-ui/store";
3
+ import { threadListLoadMoreDisabled } from "../../store/primitive-predicates";
3
4
 
4
5
  export const useThreadListLoadMore = () => {
5
6
  const aui = useAui();
6
- const disabled = useAuiState(
7
- (s) => !s.threads.hasMore || s.threads.isLoading || s.threads.isLoadingMore,
8
- );
7
+ const disabled = useAuiState(threadListLoadMoreDisabled);
9
8
 
10
9
  const loadMore = useCallback(() => {
11
10
  aui.threads.loadMore();
@@ -0,0 +1,76 @@
1
+ /** @vitest-environment jsdom */
2
+ import { render, screen } from "@testing-library/react";
3
+ import { useState, type ReactNode } from "react";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import type { Attachment } from "../../../types/attachment";
6
+
7
+ const mocks = vi.hoisted(() => ({
8
+ attachments: [] as Attachment[],
9
+ aui: {
10
+ composer: {
11
+ attachment: ({ index }: { index: number }) => ({
12
+ getState: () => mocks.attachments[index],
13
+ }),
14
+ },
15
+ },
16
+ }));
17
+
18
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
19
+ ...(await importOriginal<typeof import("@assistant-ui/store")>()),
20
+ useAui: () => mocks.aui,
21
+ useAuiState: (selector: (state: unknown) => unknown) =>
22
+ selector({ composer: { attachments: mocks.attachments } }),
23
+ RenderChildrenWithAccessor: ({
24
+ getItemState,
25
+ children,
26
+ }: {
27
+ getItemState: (aui: typeof mocks.aui) => Attachment;
28
+ children: (getItem: () => Attachment) => ReactNode;
29
+ }) => children(() => getItemState(mocks.aui)),
30
+ }));
31
+
32
+ vi.mock("../../providers/AttachmentByIndexProvider", () => ({
33
+ ComposerAttachmentByIndexProvider: ({ children }: { children: ReactNode }) =>
34
+ children,
35
+ }));
36
+
37
+ import { ComposerPrimitiveAttachments } from "./ComposerAttachments";
38
+
39
+ const attachment = (id: string, name: string): Attachment => ({
40
+ id,
41
+ name,
42
+ type: "file",
43
+ contentType: "text/plain",
44
+ status: { type: "complete" },
45
+ content: [],
46
+ });
47
+
48
+ const StatefulAttachment = ({ attachment }: { attachment: Attachment }) => {
49
+ const [initialName] = useState(attachment.name);
50
+ return <span>{initialName}</span>;
51
+ };
52
+
53
+ describe("ComposerPrimitiveAttachments", () => {
54
+ it("does not reuse component state for a different attachment", () => {
55
+ mocks.attachments = [
56
+ attachment("first", "first.txt"),
57
+ attachment("second", "second.txt"),
58
+ ];
59
+
60
+ const view = render(
61
+ <ComposerPrimitiveAttachments>
62
+ {({ attachment }) => <StatefulAttachment attachment={attachment} />}
63
+ </ComposerPrimitiveAttachments>,
64
+ );
65
+
66
+ mocks.attachments = [mocks.attachments[1]!];
67
+ view.rerender(
68
+ <ComposerPrimitiveAttachments>
69
+ {({ attachment }) => <StatefulAttachment attachment={attachment} />}
70
+ </ComposerPrimitiveAttachments>,
71
+ );
72
+
73
+ expect(screen.queryByText("second.txt")).not.toBeNull();
74
+ expect(screen.queryByText("first.txt")).toBeNull();
75
+ });
76
+ });
@@ -7,6 +7,7 @@ import {
7
7
  useMemo,
8
8
  } from "react";
9
9
  import { RenderChildrenWithAccessor, useAuiState } from "@assistant-ui/store";
10
+ import { useShallowSelector } from "@assistant-ui/store/internal";
10
11
  import { ComposerAttachmentByIndexProvider } from "../../providers/AttachmentByIndexProvider";
11
12
 
12
13
  type ComposerAttachmentsComponentConfig = {
@@ -91,12 +92,16 @@ ComposerPrimitiveAttachmentByIndex.displayName =
91
92
  const ComposerPrimitiveAttachmentsInner: FC<{
92
93
  children: (value: { attachment: Attachment }) => ReactNode;
93
94
  }> = ({ children }) => {
94
- const attachmentsCount = useAuiState((s) => s.composer.attachments.length);
95
+ const attachmentIds = useAuiState(
96
+ useShallowSelector((s) =>
97
+ s.composer.attachments.map((attachment) => attachment.id),
98
+ ),
99
+ );
95
100
 
96
101
  return useMemo(
97
102
  () =>
98
- Array.from({ length: attachmentsCount }, (_, index) => (
99
- <ComposerAttachmentByIndexProvider key={index} index={index}>
103
+ attachmentIds.map((attachmentId, index) => (
104
+ <ComposerAttachmentByIndexProvider key={attachmentId} index={index}>
100
105
  <RenderChildrenWithAccessor
101
106
  getItemState={(aui) =>
102
107
  aui.composer.attachment({ index }).getState()
@@ -112,7 +117,7 @@ const ComposerPrimitiveAttachmentsInner: FC<{
112
117
  </RenderChildrenWithAccessor>
113
118
  </ComposerAttachmentByIndexProvider>
114
119
  )),
115
- [attachmentsCount, children],
120
+ [attachmentIds, children],
116
121
  );
117
122
  };
118
123
 
@@ -0,0 +1,72 @@
1
+ /** @vitest-environment jsdom */
2
+ import { render, screen } from "@testing-library/react";
3
+ import { useState, type ReactNode } from "react";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import type { QueueItemState } from "../../../runtime/queue/queue-item";
6
+
7
+ const mocks = vi.hoisted(() => ({
8
+ queue: [] as QueueItemState[],
9
+ aui: {
10
+ composer: {
11
+ queueItem: ({ index }: { index: number }) => ({
12
+ getState: () => mocks.queue[index],
13
+ }),
14
+ },
15
+ },
16
+ }));
17
+
18
+ vi.mock("@assistant-ui/store", async (importOriginal) => ({
19
+ ...(await importOriginal<typeof import("@assistant-ui/store")>()),
20
+ useAui: () => mocks.aui,
21
+ useAuiState: (selector: (state: unknown) => unknown) =>
22
+ selector({ composer: { queue: mocks.queue } }),
23
+ RenderChildrenWithAccessor: ({
24
+ getItemState,
25
+ children,
26
+ }: {
27
+ getItemState: (aui: typeof mocks.aui) => QueueItemState;
28
+ children: (getItem: () => QueueItemState) => ReactNode;
29
+ }) => children(() => getItemState(mocks.aui)),
30
+ }));
31
+
32
+ vi.mock("../../providers/QueueItemByIndexProvider", () => ({
33
+ QueueItemByIndexProvider: ({ children }: { children: ReactNode }) => children,
34
+ }));
35
+
36
+ import { ComposerPrimitiveQueue } from "./ComposerQueue";
37
+
38
+ const queueItem = (id: string, prompt: string): QueueItemState => ({
39
+ id,
40
+ prompt,
41
+ parts: [{ type: "text", text: prompt }],
42
+ });
43
+
44
+ const StatefulQueueItem = ({ queueItem }: { queueItem: QueueItemState }) => {
45
+ const [initialPrompt] = useState(queueItem.prompt);
46
+ return <span>{initialPrompt}</span>;
47
+ };
48
+
49
+ describe("ComposerPrimitiveQueue", () => {
50
+ it("does not reuse component state for a different queue item", () => {
51
+ mocks.queue = [
52
+ queueItem("first", "first message"),
53
+ queueItem("second", "second message"),
54
+ ];
55
+
56
+ const view = render(
57
+ <ComposerPrimitiveQueue>
58
+ {({ queueItem }) => <StatefulQueueItem queueItem={queueItem} />}
59
+ </ComposerPrimitiveQueue>,
60
+ );
61
+
62
+ mocks.queue = [mocks.queue[1]!];
63
+ view.rerender(
64
+ <ComposerPrimitiveQueue>
65
+ {({ queueItem }) => <StatefulQueueItem queueItem={queueItem} />}
66
+ </ComposerPrimitiveQueue>,
67
+ );
68
+
69
+ expect(screen.queryByText("second message")).not.toBeNull();
70
+ expect(screen.queryByText("first message")).toBeNull();
71
+ });
72
+ });
@@ -1,5 +1,6 @@
1
1
  import { type FC, type ReactNode, memo, useMemo } from "react";
2
2
  import { RenderChildrenWithAccessor, useAuiState } from "@assistant-ui/store";
3
+ import { useShallowSelector } from "@assistant-ui/store/internal";
3
4
  import type { QueueItemState } from "../../../store/scopes/queue-item";
4
5
  import { QueueItemByIndexProvider } from "../../providers/QueueItemByIndexProvider";
5
6
 
@@ -13,12 +14,14 @@ export namespace ComposerPrimitiveQueue {
13
14
  const ComposerPrimitiveQueueInner: FC<{
14
15
  children: (value: { queueItem: QueueItemState }) => ReactNode;
15
16
  }> = ({ children }) => {
16
- const queue = useAuiState((s) => s.composer.queue.length);
17
+ const queueItemIds = useAuiState(
18
+ useShallowSelector((s) => s.composer.queue.map((item) => item.id)),
19
+ );
17
20
 
18
21
  return useMemo(
19
22
  () =>
20
- Array.from({ length: queue }, (_, index) => (
21
- <QueueItemByIndexProvider key={index} index={index}>
23
+ queueItemIds.map((queueItemId, index) => (
24
+ <QueueItemByIndexProvider key={queueItemId} index={index}>
22
25
  <RenderChildrenWithAccessor
23
26
  getItemState={(aui) => aui.composer.queueItem({ index }).getState()}
24
27
  >
@@ -32,7 +35,7 @@ const ComposerPrimitiveQueueInner: FC<{
32
35
  </RenderChildrenWithAccessor>
33
36
  </QueueItemByIndexProvider>
34
37
  )),
35
- [queue, children],
38
+ [queueItemIds, children],
36
39
  );
37
40
  };
38
41