@assistant-ui/react 0.15.16 → 0.15.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 (223) hide show
  1. package/README.md +3 -3
  2. package/dist/index.d.ts +4 -3
  3. package/dist/index.js +2 -2
  4. package/dist/mcp-apps/McpAppRenderer.d.ts +6 -1
  5. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  6. package/dist/mcp-apps/McpAppRenderer.js +19 -12
  7. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  8. package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
  9. package/dist/mcp-apps/McpAppsRemoteHost.js +20 -17
  10. package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
  11. package/dist/mcp-apps/app-frame.d.ts.map +1 -1
  12. package/dist/mcp-apps/app-frame.js +189 -94
  13. package/dist/mcp-apps/app-frame.js.map +1 -1
  14. package/dist/mcp-apps/index.d.ts +2 -2
  15. package/dist/model-context/frame/useAssistantFrameHost.d.ts +1 -1
  16. package/dist/model-context/frame/useAssistantFrameHost.js +10 -11
  17. package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
  18. package/dist/model-context/makeAssistantVisible.js +1 -1
  19. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  20. package/dist/primitives/actionBar/ActionBarCopy.js +3 -3
  21. package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
  22. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +3 -3
  23. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  24. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +3 -3
  25. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
  26. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +3 -3
  27. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
  28. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
  29. package/dist/primitives/actionBar/ActionBarStopSpeaking.js +3 -3
  30. package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
  31. package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
  32. package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -1
  33. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -1
  34. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -1
  35. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -1
  36. package/dist/primitives/actionBarMore/scope.d.ts +2 -2
  37. package/dist/primitives/actionBarMore/scope.d.ts.map +1 -1
  38. package/dist/primitives/actionBarMore/scope.js.map +1 -1
  39. package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -1
  40. package/dist/primitives/assistantModal/AssistantModalContent.js +1 -1
  41. package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
  42. package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
  43. package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -1
  44. package/dist/primitives/assistantModal/scope.d.ts +2 -2
  45. package/dist/primitives/assistantModal/scope.d.ts.map +1 -1
  46. package/dist/primitives/assistantModal/scope.js.map +1 -1
  47. package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
  48. package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
  49. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  50. package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
  51. package/dist/primitives/composer/ComposerInput.js +1 -3
  52. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  53. package/dist/primitives/composer/ComposerQuote.js +5 -5
  54. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  55. package/dist/primitives/composer/ComposerRoot.js +3 -3
  56. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  57. package/dist/primitives/composer/trigger/TriggerPopover.d.ts +5 -0
  58. package/dist/primitives/composer/trigger/TriggerPopover.d.ts.map +1 -1
  59. package/dist/primitives/composer/trigger/TriggerPopover.js +157 -130
  60. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  61. package/dist/primitives/composer/trigger/TriggerPopoverBack.js +3 -3
  62. package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -1
  63. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +4 -4
  64. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
  65. package/dist/primitives/composer/trigger/TriggerPopoverItems.js +4 -4
  66. package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
  67. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts +2 -0
  68. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
  69. package/dist/primitives/composer/trigger/TriggerPopoverResource.js +63 -61
  70. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
  71. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts +2 -0
  72. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.d.ts.map +1 -1
  73. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
  74. package/dist/primitives/composer/trigger/detectTrigger.d.ts +21 -1
  75. package/dist/primitives/composer/trigger/detectTrigger.d.ts.map +1 -0
  76. package/dist/primitives/composer/trigger/detectTrigger.js +17 -3
  77. package/dist/primitives/composer/trigger/detectTrigger.js.map +1 -1
  78. package/dist/primitives/composer/trigger/index.d.ts +3 -1
  79. package/dist/primitives/composer/trigger/index.d.ts.map +1 -1
  80. package/dist/primitives/composer/trigger/index.js.map +1 -1
  81. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts +3 -4
  82. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
  83. package/dist/primitives/composer/trigger/triggerDetectionResource.js +15 -14
  84. package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
  85. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  86. package/dist/primitives/composer/trigger/triggerSelectionResource.js +18 -19
  87. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  88. package/dist/primitives/composer.d.ts +3 -2
  89. package/dist/primitives/composer.js.map +1 -1
  90. package/dist/primitives/error/ErrorMessage.js.map +1 -1
  91. package/dist/primitives/message/MessageIf.js.map +1 -1
  92. package/dist/primitives/message/MessageParts.js.map +1 -1
  93. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  94. package/dist/primitives/message/MessageRoot.js +4 -4
  95. package/dist/primitives/message/MessageRoot.js.map +1 -1
  96. package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
  97. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +3 -3
  98. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  99. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +3 -3
  100. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  101. package/dist/primitives/thread/ThreadIf.js.map +1 -1
  102. package/dist/primitives/thread/ThreadViewport.js +3 -3
  103. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  104. package/dist/primitives/thread/ThreadViewportFooter.js +3 -3
  105. package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
  106. package/dist/primitives/thread/useThreadViewportAutoScroll.js +1 -1
  107. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  108. package/dist/primitives/threadList/ThreadListNew.js +3 -3
  109. package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
  110. package/dist/primitives/threadListFocusGroup.d.ts +2 -1
  111. package/dist/primitives/threadListFocusGroup.d.ts.map +1 -1
  112. package/dist/primitives/threadListFocusGroup.js +2 -2
  113. package/dist/primitives/threadListFocusGroup.js.map +1 -1
  114. package/dist/primitives/threadListItem/ThreadListItemRoot.js +3 -3
  115. package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
  116. package/dist/primitives/threadListItem/ThreadListItemTrigger.js +1 -1
  117. package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
  118. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +1 -1
  119. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
  120. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -1
  121. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js +1 -1
  122. package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
  123. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -1
  124. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +1 -2
  125. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
  126. package/dist/primitives/threadListItemMore/scope.d.ts +2 -2
  127. package/dist/primitives/threadListItemMore/scope.d.ts.map +1 -1
  128. package/dist/primitives/threadListItemMore/scope.js.map +1 -1
  129. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
  130. package/dist/sandbox-host/SandboxHost.js +137 -70
  131. package/dist/sandbox-host/SandboxHost.js.map +1 -1
  132. package/dist/unstable/useLiveCompletionAdapter.js +3 -1
  133. package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
  134. package/dist/unstable/useSlashCommandAdapter.js +41 -27
  135. package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
  136. package/dist/utils/Primitive.d.ts +2 -2
  137. package/dist/utils/Primitive.js +2 -2
  138. package/dist/utils/Primitive.js.map +1 -1
  139. package/dist/utils/createActionButton.js +3 -3
  140. package/dist/utils/createActionButton.js.map +1 -1
  141. package/dist/utils/hooks/useOnResizeContent.js +1 -1
  142. package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
  143. package/dist/utils/hooks/useOnScrollToBottom.js +1 -1
  144. package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
  145. package/dist/utils/smooth/SmoothContext.d.ts +24 -0
  146. package/dist/utils/smooth/SmoothContext.d.ts.map +1 -1
  147. package/dist/utils/smooth/useSmooth.js +1 -1
  148. package/dist/utils/smooth/useSmooth.js.map +1 -1
  149. package/dist/utils/useToolArgsFieldStatus.d.ts +5 -1
  150. package/package.json +11 -19
  151. package/src/index.ts +9 -1
  152. package/src/mcp-apps/McpAppRenderer.test.tsx +133 -4
  153. package/src/mcp-apps/McpAppRenderer.tsx +28 -21
  154. package/src/mcp-apps/McpAppsRemoteHost.concurrent.test.tsx +215 -0
  155. package/src/mcp-apps/McpAppsRemoteHost.ts +28 -12
  156. package/src/mcp-apps/app-frame.test.tsx +102 -0
  157. package/src/mcp-apps/app-frame.tsx +17 -4
  158. package/src/mcp-apps/index.ts +1 -0
  159. package/src/model-context/frame/AssistantFrame.test.ts +3 -3
  160. package/src/model-context/frame/SPEC_AssistantFrame.md +1 -1
  161. package/src/model-context/frame/useAssistantFrameHost.ts +2 -2
  162. package/src/model-context/makeAssistantVisible.tsx +1 -1
  163. package/src/primitives/actionBar/ActionBarCopy.tsx +1 -1
  164. package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +1 -1
  165. package/src/primitives/actionBar/ActionBarFeedbackNegative.tsx +1 -1
  166. package/src/primitives/actionBar/ActionBarFeedbackPositive.tsx +1 -1
  167. package/src/primitives/actionBar/ActionBarStopSpeaking.tsx +1 -1
  168. package/src/primitives/actionBarMore/scope.ts +2 -2
  169. package/src/primitives/assistantModal/AssistantModalContent.tsx +1 -1
  170. package/src/primitives/assistantModal/scope.ts +2 -2
  171. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +1 -1
  172. package/src/primitives/composer/ComposerInput.test.tsx +2 -1
  173. package/src/primitives/composer/ComposerInput.tsx +3 -3
  174. package/src/primitives/composer/ComposerQuote.tsx +1 -1
  175. package/src/primitives/composer/ComposerRoot.tsx +1 -1
  176. package/src/primitives/composer/trigger/TriggerPopover.tsx +16 -1
  177. package/src/primitives/composer/trigger/TriggerPopoverBack.tsx +1 -1
  178. package/src/primitives/composer/trigger/TriggerPopoverCategories.tsx +1 -1
  179. package/src/primitives/composer/trigger/TriggerPopoverItems.tsx +1 -1
  180. package/src/primitives/composer/trigger/TriggerPopoverResource.ts +4 -1
  181. package/src/primitives/composer/trigger/TriggerPopoverRootContext.tsx +2 -0
  182. package/src/primitives/composer/trigger/detectTrigger.test.ts +88 -5
  183. package/src/primitives/composer/trigger/detectTrigger.ts +50 -9
  184. package/src/primitives/composer/trigger/index.ts +1 -0
  185. package/src/primitives/composer/trigger/triggerDetectionResource.ts +10 -7
  186. package/src/primitives/composer/trigger/triggerSelectionResource.test.ts +15 -3
  187. package/src/primitives/composer/trigger/triggerSelectionResource.ts +1 -4
  188. package/src/primitives/composer.ts +1 -0
  189. package/src/primitives/message/MessageRoot.tsx +1 -1
  190. package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +1 -1
  191. package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +1 -1
  192. package/src/primitives/thread/ThreadViewport.tsx +1 -1
  193. package/src/primitives/thread/ThreadViewportFooter.tsx +1 -1
  194. package/src/primitives/thread/useThreadViewportAutoScroll.ts +1 -1
  195. package/src/primitives/threadList/ThreadListNew.tsx +1 -1
  196. package/src/primitives/threadListFocusGroup.ts +2 -2
  197. package/src/primitives/threadListItem/ThreadListItemRoot.tsx +1 -1
  198. package/src/primitives/threadListItem/ThreadListItemTrigger.tsx +1 -1
  199. package/src/primitives/threadListItemMore/ThreadListItemMoreContent.tsx +1 -1
  200. package/src/primitives/threadListItemMore/ThreadListItemMoreRoot.tsx +1 -1
  201. package/src/primitives/threadListItemMore/ThreadListItemMoreTrigger.tsx +2 -2
  202. package/src/primitives/threadListItemMore/scope.ts +2 -2
  203. package/src/sandbox-host/SandboxHost.test.tsx +45 -1
  204. package/src/sandbox-host/SandboxHost.tsx +16 -3
  205. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +31 -1
  206. package/src/unstable/useLiveCompletionAdapter.test.tsx +60 -1
  207. package/src/unstable/useLiveCompletionAdapter.ts +4 -1
  208. package/src/unstable/useSlashCommandAdapter.test.tsx +141 -0
  209. package/src/unstable/useSlashCommandAdapter.ts +52 -17
  210. package/src/utils/Primitive.tsx +2 -2
  211. package/src/utils/createActionButton.tsx +1 -1
  212. package/src/utils/hooks/useOnResizeContent.ts +1 -1
  213. package/src/utils/hooks/useOnScrollToBottom.ts +1 -1
  214. package/src/utils/smooth/useSmooth.ts +1 -1
  215. package/dist/legacy-runtime/runtime/MessagePartRuntime.d.ts +0 -3
  216. package/dist/legacy-runtime/runtime/MessagePartRuntime.js +0 -2
  217. package/dist/legacy-runtime/runtime/MessageRuntime.d.ts +0 -3
  218. package/dist/legacy-runtime/runtime/MessageRuntime.js +0 -2
  219. package/dist/legacy-runtime/runtime/ThreadListRuntime.d.ts +0 -3
  220. package/dist/legacy-runtime/runtime/ThreadListRuntime.js +0 -2
  221. package/src/legacy-runtime/runtime/MessagePartRuntime.ts +0 -2
  222. package/src/legacy-runtime/runtime/MessageRuntime.ts +0 -3
  223. package/src/legacy-runtime/runtime/ThreadListRuntime.ts +0 -4
@@ -1,8 +1,8 @@
1
1
  import { ComponentPropsWithoutRef, ComponentRef, ElementType, ForwardRefExoticComponent, ReactElement, RefAttributes } from "react";
2
- import { Primitive as Primitive$1 } from "@radix-ui/react-primitive";
2
+ import { Primitive as Primitive$1 } from "radix-ui/internal";
3
3
  //#region src/utils/Primitive.d.ts
4
4
  /**
5
- * Thin wrapper around `@radix-ui/react-primitive` that adds `render` prop support.
5
+ * Thin wrapper around Radix `Primitive` that adds `render` prop support.
6
6
  *
7
7
  * When `render` is provided, it is converted to the equivalent `asChild` pattern:
8
8
  * render={<Comp props />} + children → asChild + <Comp props>{children}</Comp>
@@ -1,10 +1,10 @@
1
1
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
2
2
  import { cloneElement, forwardRef, isValidElement } from "@assistant-ui/tap/react-shim";
3
3
  import { jsx } from "react/jsx-runtime";
4
- import { Primitive as Primitive$1 } from "@radix-ui/react-primitive";
4
+ import { Primitive as Primitive$1 } from "radix-ui/internal";
5
5
  //#region src/utils/Primitive.tsx
6
6
  /**
7
- * Thin wrapper around `@radix-ui/react-primitive` that adds `render` prop support.
7
+ * Thin wrapper around Radix `Primitive` that adds `render` prop support.
8
8
  *
9
9
  * When `render` is provided, it is converted to the equivalent `asChild` pattern:
10
10
  * render={<Comp props />} + children → asChild + <Comp props>{children}</Comp>
@@ -1 +1 @@
1
- {"version":3,"file":"Primitive.js","names":["ComponentPropsWithoutRef","ComponentRef","ElementType","ForwardRefExoticComponent","PropsWithoutRef","ReactElement","ReactNode","RefAttributes","cloneElement","forwardRef","isValidElement","Primitive","RadixPrimitive","NODES","const","PrimitiveNode","WithRenderPropProps","T","render","PrimitiveProps","E","WithRenderPropRuntimeProps","asChild","children","PrimitiveRef","withRenderProp","Component","Wrapped","t0","ref","$","_c","rest","Comp","renderChildren","undefined","props","Record","t1","t2","t3","componentName","displayName","name","createPrimitive","node","RadixComp","reduce","acc","ReturnType","K"],"sources":["../../src/utils/Primitive.tsx"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n type ElementType,\n type ForwardRefExoticComponent,\n type PropsWithoutRef,\n type ReactElement,\n type ReactNode,\n type RefAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n} from \"react\";\nimport { Primitive as RadixPrimitive } from \"@radix-ui/react-primitive\";\n\n/**\n * Thin wrapper around `@radix-ui/react-primitive` that adds `render` prop support.\n *\n * When `render` is provided, it is converted to the equivalent `asChild` pattern:\n * render={<Comp props />} + children → asChild + <Comp props>{children}</Comp>\n *\n * All prop merging, ref composition, and event handler chaining remain handled\n * by Radix's battle-tested Slot implementation — we add zero custom logic for that.\n */\n\n// Match @radix-ui/react-primitive's full element set\nconst NODES = [\n \"a\",\n \"button\",\n \"div\",\n \"form\",\n \"h2\",\n \"h3\",\n \"img\",\n \"input\",\n \"label\",\n \"li\",\n \"nav\",\n \"ol\",\n \"p\",\n \"select\",\n \"span\",\n \"svg\",\n \"ul\",\n] as const;\ntype PrimitiveNode = (typeof NODES)[number];\n\ntype WithRenderPropProps<T extends ElementType> =\n ComponentPropsWithoutRef<T> & {\n render?: ReactElement | undefined;\n };\n\ntype PrimitiveProps<E extends PrimitiveNode> = WithRenderPropProps<\n (typeof RadixPrimitive)[E]\n>;\n\ntype WithRenderPropRuntimeProps<T extends ElementType> =\n WithRenderPropProps<T> & {\n asChild?: boolean | undefined;\n children?: ReactNode | undefined;\n };\n\ntype PrimitiveRef<E extends PrimitiveNode> = ComponentRef<\n (typeof RadixPrimitive)[E]\n>;\n\nfunction withRenderProp<T extends ElementType>(Component: T) {\n const Wrapped = forwardRef<ComponentRef<T>, WithRenderPropRuntimeProps<T>>(\n (\n {\n render,\n asChild,\n children,\n ...rest\n }: PropsWithoutRef<WithRenderPropRuntimeProps<T>>,\n ref,\n ) => {\n const Comp = Component as any;\n\n if (render && isValidElement(render)) {\n const renderChildren =\n children !== undefined\n ? children\n : ((render.props as Record<string, unknown>).children as ReactNode);\n\n return (\n <Comp {...(rest as any)} asChild ref={ref}>\n {cloneElement(render, undefined, renderChildren)}\n </Comp>\n );\n }\n\n return (\n <Comp {...(rest as any)} asChild={asChild} ref={ref}>\n {children}\n </Comp>\n );\n },\n );\n\n const componentName =\n typeof Component === \"string\"\n ? Component\n : (Component.displayName ?? Component.name ?? \"Component\");\n Wrapped.displayName = componentName;\n\n return Wrapped as ForwardRefExoticComponent<\n WithRenderPropProps<T> & RefAttributes<ComponentRef<T>>\n >;\n}\n\nfunction createPrimitive<E extends PrimitiveNode>(node: E) {\n const RadixComp = RadixPrimitive[node];\n const Component = withRenderProp(RadixComp);\n\n Component.displayName = `Primitive.${node}`;\n return Component as ForwardRefExoticComponent<\n PrimitiveProps<E> & RefAttributes<PrimitiveRef<E>>\n >;\n}\n\nconst Primitive = NODES.reduce(\n (acc, node) => {\n acc[node] = createPrimitive(node);\n return acc;\n },\n {} as {\n [K in PrimitiveNode]: ReturnType<typeof createPrimitive<K>>;\n },\n);\n\nexport { Primitive, withRenderProp };\nexport type { PrimitiveProps, WithRenderPropProps };\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAMa,QAAQ;CACZ;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAAI;AAuBN,SAASY,eAAsCC,WAAc;CAC3D,MAAMC,UAAUlB,YACdmB,IAAAC,QAAA;EAAA,MAAAC,IAAAC,EAAA,EAAA;EAAA,IAAAT;EAAA,IAAAC;EAAA,IAAAL;EAAA,IAAAc;EAAA,IAAAF,EAAA,OAAAF,IAAA;GACE,CAAA,CAAAV,QAAAI,SAAAC,aAAAS,QAAAJ;GAKiDE,EAAA,KAAAF;GAAAE,EAAA,KAAAR;GAAAQ,EAAA,KAAAP;GAAAO,EAAA,KAAAZ;GAAAY,EAAA,KAAAE;EAAA,OAAA;GAAAV,UAAAQ,EAAA;GAAAP,WAAAO,EAAA;GAAAZ,SAAAY,EAAA;GAAAE,OAAAF,EAAA;EAAA;EAGjD,MAAAG,OAAaP;EAEb,IAAIR,UAAUR,eAAeQ,MAAM,GAAC;GAClC,MAAAgB,iBACEX,aAAaY,KAAAA,IAAbZ,WAEML,OAAMkB,MAAiCb;GAGlC,MAAAe,KAAAN;GAAW,IAAAO;GAAA,IAAAT,EAAA,OAAAZ,UAAAY,EAAA,OAAAI,gBAAA;IACnBK,KAAA/B,aAAaU,QAAQiB,KAAAA,GAAWD,cAAc;IAACJ,EAAA,KAAAZ;IAAAY,EAAA,KAAAI;IAAAJ,EAAA,KAAAS;GAAA,OAAAA,KAAAT,EAAA;GAAA,IAAAU;GAAA,IAAAV,EAAA,OAAAD,OAAAC,EAAA,OAAAQ,MAAAR,EAAA,QAAAS,IAAA;IADlDC,KAAA,oBAAC,MAAD;KAAK,GAAMF;KAAc,SAAA;KAAaT;KACnCU,UAAAA;IADE,CAAA;IAEET,EAAA,KAAAD;IAAAC,EAAA,KAAAQ;IAAAR,EAAA,MAAAS;IAAAT,EAAA,MAAAU;GAAA,OAAAA,KAAAV,EAAA;GAAA,OAFPU;EAEO;EAKE,MAAAF,KAAAN;EAAW,IAAAO;EAAA,IAAAT,EAAA,QAAAR,WAAAQ,EAAA,QAAAP,YAAAO,EAAA,QAAAD,OAAAC,EAAA,QAAAQ,IAAA;GAAtBC,KAAA,oBAAC,MAAD;IAAK,GAAMD;IAAuBhB;IAAcO;IAC7CN;GADE,CAAA;GAEEO,EAAA,MAAAR;GAAAQ,EAAA,MAAAP;GAAAO,EAAA,MAAAD;GAAAC,EAAA,MAAAQ;GAAAR,EAAA,MAAAS;EAAA,OAAAA,KAAAT,EAAA;EAAA,OAFPS;CAEO,CAGb;CAMAZ,QAAQe,cAHN,OAAOhB,cAAc,WACjBA,YACCA,UAAUgB,eAAehB,UAAUiB,QAAQ;CAGlD,OAAOhB;AAGT;AAEA,SAASiB,gBAAyCC,MAAS;CACzD,MAAMC,YAAYlC,YAAeiC;CACjC,MAAMnB,YAAYD,eAAeqB,SAAS;CAE1CpB,UAAUgB,cAAc,aAAaG;CACrC,OAAOnB;AAGT;AAEA,MAAMf,YAAYE,MAAMkC,QACrBC,KAAKH,SAAS;CACbG,IAAIH,QAAQD,gBAAgBC,IAAI;CAChC,OAAOG;AACT,GACA,CAAC,CAGH"}
1
+ {"version":3,"file":"Primitive.js","names":["ComponentPropsWithoutRef","ComponentRef","ElementType","ForwardRefExoticComponent","PropsWithoutRef","ReactElement","ReactNode","RefAttributes","cloneElement","forwardRef","isValidElement","Primitive","RadixPrimitive","NODES","const","PrimitiveNode","WithRenderPropProps","T","render","PrimitiveProps","E","WithRenderPropRuntimeProps","asChild","children","PrimitiveRef","withRenderProp","Component","Wrapped","t0","ref","$","_c","rest","Comp","renderChildren","undefined","props","Record","t1","t2","t3","componentName","displayName","name","createPrimitive","node","RadixComp","reduce","acc","ReturnType","K"],"sources":["../../src/utils/Primitive.tsx"],"sourcesContent":["import {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n type ElementType,\n type ForwardRefExoticComponent,\n type PropsWithoutRef,\n type ReactElement,\n type ReactNode,\n type RefAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n} from \"react\";\nimport { Primitive as RadixPrimitive } from \"radix-ui/internal\";\n\n/**\n * Thin wrapper around Radix `Primitive` that adds `render` prop support.\n *\n * When `render` is provided, it is converted to the equivalent `asChild` pattern:\n * render={<Comp props />} + children → asChild + <Comp props>{children}</Comp>\n *\n * All prop merging, ref composition, and event handler chaining remain handled\n * by Radix's battle-tested Slot implementation — we add zero custom logic for that.\n */\n\n// Match @radix-ui/react-primitive's full element set\nconst NODES = [\n \"a\",\n \"button\",\n \"div\",\n \"form\",\n \"h2\",\n \"h3\",\n \"img\",\n \"input\",\n \"label\",\n \"li\",\n \"nav\",\n \"ol\",\n \"p\",\n \"select\",\n \"span\",\n \"svg\",\n \"ul\",\n] as const;\ntype PrimitiveNode = (typeof NODES)[number];\n\ntype WithRenderPropProps<T extends ElementType> =\n ComponentPropsWithoutRef<T> & {\n render?: ReactElement | undefined;\n };\n\ntype PrimitiveProps<E extends PrimitiveNode> = WithRenderPropProps<\n (typeof RadixPrimitive)[E]\n>;\n\ntype WithRenderPropRuntimeProps<T extends ElementType> =\n WithRenderPropProps<T> & {\n asChild?: boolean | undefined;\n children?: ReactNode | undefined;\n };\n\ntype PrimitiveRef<E extends PrimitiveNode> = ComponentRef<\n (typeof RadixPrimitive)[E]\n>;\n\nfunction withRenderProp<T extends ElementType>(Component: T) {\n const Wrapped = forwardRef<ComponentRef<T>, WithRenderPropRuntimeProps<T>>(\n (\n {\n render,\n asChild,\n children,\n ...rest\n }: PropsWithoutRef<WithRenderPropRuntimeProps<T>>,\n ref,\n ) => {\n const Comp = Component as any;\n\n if (render && isValidElement(render)) {\n const renderChildren =\n children !== undefined\n ? children\n : ((render.props as Record<string, unknown>).children as ReactNode);\n\n return (\n <Comp {...(rest as any)} asChild ref={ref}>\n {cloneElement(render, undefined, renderChildren)}\n </Comp>\n );\n }\n\n return (\n <Comp {...(rest as any)} asChild={asChild} ref={ref}>\n {children}\n </Comp>\n );\n },\n );\n\n const componentName =\n typeof Component === \"string\"\n ? Component\n : (Component.displayName ?? Component.name ?? \"Component\");\n Wrapped.displayName = componentName;\n\n return Wrapped as ForwardRefExoticComponent<\n WithRenderPropProps<T> & RefAttributes<ComponentRef<T>>\n >;\n}\n\nfunction createPrimitive<E extends PrimitiveNode>(node: E) {\n const RadixComp = RadixPrimitive[node];\n const Component = withRenderProp(RadixComp);\n\n Component.displayName = `Primitive.${node}`;\n return Component as ForwardRefExoticComponent<\n PrimitiveProps<E> & RefAttributes<PrimitiveRef<E>>\n >;\n}\n\nconst Primitive = NODES.reduce(\n (acc, node) => {\n acc[node] = createPrimitive(node);\n return acc;\n },\n {} as {\n [K in PrimitiveNode]: ReturnType<typeof createPrimitive<K>>;\n },\n);\n\nexport { Primitive, withRenderProp };\nexport type { PrimitiveProps, WithRenderPropProps };\n"],"mappings":";;;;;;;;;;;;;;AA0BA,MAAMa,QAAQ;CACZ;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAAI;AAuBN,SAASY,eAAsCC,WAAc;CAC3D,MAAMC,UAAUlB,YACdmB,IAAAC,QAAA;EAAA,MAAAC,IAAAC,EAAA,EAAA;EAAA,IAAAT;EAAA,IAAAC;EAAA,IAAAL;EAAA,IAAAc;EAAA,IAAAF,EAAA,OAAAF,IAAA;GACE,CAAA,CAAAV,QAAAI,SAAAC,UAAA,GAAAS,QAAAJ;GAKiDE,EAAA,KAAAF;GAAAE,EAAA,KAAAR;GAAAQ,EAAA,KAAAP;GAAAO,EAAA,KAAAZ;GAAAY,EAAA,KAAAE;EAAA,OAAA;GAAAV,UAAAQ,EAAA;GAAAP,WAAAO,EAAA;GAAAZ,SAAAY,EAAA;GAAAE,OAAAF,EAAA;EAAA;EAGjD,MAAAG,OAAaP;EAEb,IAAIR,UAAUR,eAAeQ,MAAM,GAAC;GAClC,MAAAgB,iBACEX,aAAaY,KAAAA,IAAbZ,WAEML,OAAMkB,MAAiCb;GAGlC,MAAAe,KAAAN;GAAW,IAAAO;GAAA,IAAAT,EAAA,OAAAZ,UAAAY,EAAA,OAAAI,gBAAA;IACnBK,KAAA/B,aAAaU,QAAQiB,KAAAA,GAAWD,cAAc;IAACJ,EAAA,KAAAZ;IAAAY,EAAA,KAAAI;IAAAJ,EAAA,KAAAS;GAAA,OAAAA,KAAAT,EAAA;GAAA,IAAAU;GAAA,IAAAV,EAAA,OAAAD,OAAAC,EAAA,OAAAQ,MAAAR,EAAA,QAAAS,IAAA;IADlDC,KAAA,oBAAC,MAAD;KAAK,GAAMF;KAAc,SAAA;KAAaT;KACnCU,UAAAA;IADE,CAAA;IAEET,EAAA,KAAAD;IAAAC,EAAA,KAAAQ;IAAAR,EAAA,MAAAS;IAAAT,EAAA,MAAAU;GAAA,OAAAA,KAAAV,EAAA;GAAA,OAFPU;EAEO;EAKE,MAAAF,KAAAN;EAAW,IAAAO;EAAA,IAAAT,EAAA,QAAAR,WAAAQ,EAAA,QAAAP,YAAAO,EAAA,QAAAD,OAAAC,EAAA,QAAAQ,IAAA;GAAtBC,KAAA,oBAAC,MAAD;IAAK,GAAMD;IAAuBhB;IAAcO;IAC7CN;GADE,CAAA;GAEEO,EAAA,MAAAR;GAAAQ,EAAA,MAAAP;GAAAO,EAAA,MAAAD;GAAAC,EAAA,MAAAQ;GAAAR,EAAA,MAAAS;EAAA,OAAAA,KAAAT,EAAA;EAAA,OAFPS;CAEO,CAGb;CAMAZ,QAAQe,cAHN,OAAOhB,cAAc,WACjBA,YACCA,UAAUgB,eAAehB,UAAUiB,QAAQ;CAGlD,OAAOhB;AAGT;AAEA,SAASiB,gBAAyCC,MAAS;CACzD,MAAMC,YAAYlC,YAAeiC;CACjC,MAAMnB,YAAYD,eAAeqB,SAAS;CAE1CpB,UAAUgB,cAAc,aAAaG;CACrC,OAAOnB;AAGT;AAEA,MAAMf,YAAYE,MAAMkC,QACrBC,KAAKH,SAAS;CACbG,IAAIH,QAAQD,gBAAgBC,IAAI;CAChC,OAAOG;AACT,GACA,CAAC,CAGH"}
@@ -1,8 +1,8 @@
1
- import { Primitive } from "./Primitive.js";
1
+ import { Primitive as Primitive$1 } from "./Primitive.js";
2
2
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
3
  import { forwardRef } from "@assistant-ui/tap/react-shim";
4
4
  import { jsx } from "react/jsx-runtime";
5
- import { composeEventHandlers } from "@radix-ui/primitive";
5
+ import { composeEventHandlers } from "radix-ui/internal";
6
6
  //#region src/utils/createActionButton.tsx
7
7
  const createActionButton = (displayName, useActionButton, forwardProps = []) => {
8
8
  const ActionButton = forwardRef((props, forwardedRef) => {
@@ -14,7 +14,7 @@ const createActionButton = (displayName, useActionButton, forwardProps = []) =>
14
14
  else primitiveProps[key] = props[key];
15
15
  });
16
16
  const callback = useActionButton(forwardedProps) ?? void 0;
17
- const t0 = Primitive;
17
+ const t0 = Primitive$1;
18
18
  const t1 = "button";
19
19
  const t2 = primitiveProps.disabled || !callback;
20
20
  const t3 = composeEventHandlers(primitiveProps.onClick, callback);
@@ -1 +1 @@
1
- {"version":3,"file":"createActionButton.js","names":["ComponentRef","forwardRef","ComponentPropsWithoutRef","MouseEventHandler","Primitive","composeEventHandlers","ActionButtonCallback","props","TProps","HTMLButtonElement","PrimitiveButtonProps","button","ActionButtonProps","THook","ActionButtonElement","createActionButton","displayName","useActionButton","forwardProps","NonNullable","ActionButton","forwardedRef","$","_c","forwardedProps","primitiveProps","Object","keys","Array","forEach","key","includes","callback","undefined","t0","t1","t2","disabled","t3","onClick","t4"],"sources":["../../src/utils/createActionButton.tsx"],"sourcesContent":["import {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEventHandler,\n} from \"react\";\nimport { Primitive } from \"./Primitive\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\n\ntype ActionButtonCallback<TProps> = (\n props: TProps,\n) => MouseEventHandler<HTMLButtonElement> | null;\n\ntype PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;\n\nexport type ActionButtonProps<THook> = PrimitiveButtonProps &\n (THook extends (props: infer TProps) => unknown ? TProps : never);\n\nexport type ActionButtonElement = ComponentRef<typeof Primitive.button>;\n\nexport const createActionButton = <TProps,>(\n displayName: string,\n useActionButton: ActionButtonCallback<TProps>,\n forwardProps: (keyof NonNullable<TProps>)[] = [],\n) => {\n const ActionButton = forwardRef<\n ActionButtonElement,\n PrimitiveButtonProps & TProps\n >((props, forwardedRef) => {\n const forwardedProps = {} as TProps;\n const primitiveProps = {} as PrimitiveButtonProps;\n\n (Object.keys(props) as Array<keyof typeof props>).forEach((key) => {\n if (forwardProps.includes(key as keyof TProps)) {\n (forwardedProps as any)[key] = props[key];\n } else {\n (primitiveProps as any)[key] = props[key];\n }\n });\n\n const callback = useActionButton(forwardedProps as TProps) ?? undefined;\n return (\n <Primitive.button\n type=\"button\"\n {...primitiveProps}\n ref={forwardedRef}\n disabled={primitiveProps.disabled || !callback}\n onClick={composeEventHandlers(primitiveProps.onClick, callback)}\n />\n );\n });\n\n ActionButton.displayName = displayName;\n\n return ActionButton;\n};\n"],"mappings":";;;;;;AAoBA,MAAae,sBACXC,aACAC,iBACAC,eAA8C,CAAA,MAC3C;CACH,MAAME,eAAenB,YAGnBM,OAAAc,iBAAA;EAAA,MAAAC,IAAAC,EAAA,CAAA;EACA,MAAAC,iBAAuB,CAAC;EACxB,MAAAC,iBAAuB,CAAC;EAExB,OAAOE,KAAMpB,KAAK,CAAC,CAA6BsB,SAAUC,QAAA;GACxD,IAAIZ,aAAYa,SAAUD,GAAmB,GAC3C,eAAwBA,OAAOvB,MAAMuB;QAErC,eAAwBA,OAAOvB,MAAMuB;EACtC,CACF;EAED,MAAAE,WAAiBf,gBAAgBO,cAAqC,KAArDS,KAAAA;EAEd,MAAAC,KAAA9B;EACM,MAAA+B,KAAA;EAGK,MAAAC,KAAAX,eAAcY,YAAd,CAA4BL;EAC7B,MAAAM,KAAAjC,qBAAqBoB,eAAcc,SAAUP,QAAQ;EAAC,IAAAQ;EAAA,IAAAlB,EAAA,OAAAD,gBAAAC,EAAA,OAAAG,kBAAAH,EAAA,OAAAY,GAAAvB,UAAAW,EAAA,OAAAc,MAAAd,EAAA,OAAAgB,IAAA;GALjEE,KAAA,oBAAA,GAAA,QAAA;IACO,MAAAL;IAAQ,GACTV;IACCJ,KAAAA;IACK,UAAAe;IACD,SAAAE;GAAsD,CAAA;GAC/DhB,EAAA,KAAAD;GAAAC,EAAA,KAAAG;GAAAH,EAAA,KAAAY,GAAAvB;GAAAW,EAAA,KAAAc;GAAAd,EAAA,KAAAgB;GAAAhB,EAAA,KAAAkB;EAAA,OAAAA,KAAAlB,EAAA;EAAA,OANFkB;CAME,CAEL;CAEDpB,aAAaJ,cAAcA;CAE3B,OAAOI;AACT"}
1
+ {"version":3,"file":"createActionButton.js","names":["ComponentRef","forwardRef","ComponentPropsWithoutRef","MouseEventHandler","Primitive","composeEventHandlers","ActionButtonCallback","props","TProps","HTMLButtonElement","PrimitiveButtonProps","button","ActionButtonProps","THook","ActionButtonElement","createActionButton","displayName","useActionButton","forwardProps","NonNullable","ActionButton","forwardedRef","$","_c","forwardedProps","primitiveProps","Object","keys","Array","forEach","key","includes","callback","undefined","t0","t1","t2","disabled","t3","onClick","t4"],"sources":["../../src/utils/createActionButton.tsx"],"sourcesContent":["import {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEventHandler,\n} from \"react\";\nimport { Primitive } from \"./Primitive\";\nimport { composeEventHandlers } from \"radix-ui/internal\";\n\ntype ActionButtonCallback<TProps> = (\n props: TProps,\n) => MouseEventHandler<HTMLButtonElement> | null;\n\ntype PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;\n\nexport type ActionButtonProps<THook> = PrimitiveButtonProps &\n (THook extends (props: infer TProps) => unknown ? TProps : never);\n\nexport type ActionButtonElement = ComponentRef<typeof Primitive.button>;\n\nexport const createActionButton = <TProps,>(\n displayName: string,\n useActionButton: ActionButtonCallback<TProps>,\n forwardProps: (keyof NonNullable<TProps>)[] = [],\n) => {\n const ActionButton = forwardRef<\n ActionButtonElement,\n PrimitiveButtonProps & TProps\n >((props, forwardedRef) => {\n const forwardedProps = {} as TProps;\n const primitiveProps = {} as PrimitiveButtonProps;\n\n (Object.keys(props) as Array<keyof typeof props>).forEach((key) => {\n if (forwardProps.includes(key as keyof TProps)) {\n (forwardedProps as any)[key] = props[key];\n } else {\n (primitiveProps as any)[key] = props[key];\n }\n });\n\n const callback = useActionButton(forwardedProps as TProps) ?? undefined;\n return (\n <Primitive.button\n type=\"button\"\n {...primitiveProps}\n ref={forwardedRef}\n disabled={primitiveProps.disabled || !callback}\n onClick={composeEventHandlers(primitiveProps.onClick, callback)}\n />\n );\n });\n\n ActionButton.displayName = displayName;\n\n return ActionButton;\n};\n"],"mappings":";;;;;;AAoBA,MAAae,sBACXC,aACAC,iBACAC,eAA8C,CAAA,MAC3C;CACH,MAAME,eAAenB,YAGnBM,OAAAc,iBAAA;EAAA,MAAAC,IAAAC,EAAA,CAAA;EACA,MAAAC,iBAAuB,CAAC;EACxB,MAAAC,iBAAuB,CAAC;EAExB,OAAOE,KAAMpB,KAAK,CAAC,CAA6BsB,SAAUC,QAAA;GACxD,IAAIZ,aAAYa,SAAUD,GAAmB,GAC3C,eAAwBA,OAAOvB,MAAMuB;QAErC,eAAwBA,OAAOvB,MAAMuB;EACtC,CACF;EAED,MAAAE,WAAiBf,gBAAgBO,cAAqC,KAArDS,KAAAA;EAEd,MAAAC,KAAA9B;EACM,MAAA+B,KAAA;EAGK,MAAAC,KAAAX,eAAcY,YAAd,CAA4BL;EAC7B,MAAAM,KAAAjC,qBAAqBoB,eAAcc,SAAUP,QAAQ;EAAC,IAAAQ;EAAA,IAAAlB,EAAA,OAAAD,gBAAAC,EAAA,OAAAG,kBAAAH,EAAA,OAAAY,GAAAvB,UAAAW,EAAA,OAAAc,MAAAd,EAAA,OAAAgB,IAAA;GALjEE,KAAA,oBAAA,GAAA,QAAA;IACO,MAAAL;IAAQ,GACTV;IACCJ,KAAAA;IACK,UAAAe;IACD,SAAAE;GAAsD,CAAA;GAC/DhB,EAAA,KAAAD;GAAAC,EAAA,KAAAG;GAAAH,EAAA,KAAAY,GAAAvB;GAAAW,EAAA,KAAAc;GAAAd,EAAA,KAAAgB;GAAAhB,EAAA,KAAAkB;EAAA,OAAAA,KAAAlB,EAAA;EAAA,OANFkB;CAME,CAEL;CAEDpB,aAAaJ,cAAcA;CAE3B,OAAOI;AACT"}
@@ -1,7 +1,7 @@
1
1
  import { useManagedRef } from "./useManagedRef.js";
2
2
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
3
  import "@assistant-ui/tap/react-shim";
4
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
4
+ import { useCallbackRef } from "radix-ui/internal";
5
5
  //#region src/utils/hooks/useOnResizeContent.ts
6
6
  const useOnResizeContent = (callback) => {
7
7
  const $ = c(2);
@@ -1 +1 @@
1
- {"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","resizeObserver","ResizeObserver","mutationObserver","MutationObserver","mutations","hasRelevantMutation","some","_temp","observe","childList","subtree","attributes","characterData","disconnect","refCallback","m","type","attributeName"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => {\n const resizeObserver = new ResizeObserver(() => {\n callbackRef();\n });\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // with code paths that write styles in response to viewport changes.\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n callbackRef();\n }\n });\n\n resizeObserver.observe(el);\n mutationObserver.observe(el, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n },\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;;AAIA,MAAaG,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAA;GACE,MAAAC,iBAAuB,IAAIC,qBAAe;IACxCJ,YAAY;GAAC,CACd;GAED,MAAAK,mBAAyB,IAAIC,kBAAiBC,cAAA;IAM5C,IAH4BA,UAASE,KACnCC,KAEEF,GACFR,YAAY;GACb,CACF;GAEDG,eAAcQ,QAAST,EAAE;GACzBG,iBAAgBM,QAAST,IAAI;IAAAU,WAChB;IAAIC,SACN;IAAIC,YACD;IAAIC,eACD;GACjB,CAAC;GAAC,aAEK;IACLZ,eAAca,WAAY;IAC1BX,iBAAgBW,WAAY;GAAC;EAC9B;EACFlB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAED,OAEKH,cAAcsB,EAAW;AAAC;AApCD,SAAAP,MAAAQ,GAAA;CAAA,OAajBA,EAACC,SAAU,gBAAgBD,EAACE,kBAAmB;AAAO"}
1
+ {"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","resizeObserver","ResizeObserver","mutationObserver","MutationObserver","mutations","hasRelevantMutation","some","_temp","observe","childList","subtree","attributes","characterData","disconnect","refCallback","m","type","attributeName"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { useCallbackRef } from \"radix-ui/internal\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => {\n const resizeObserver = new ResizeObserver(() => {\n callbackRef();\n });\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // with code paths that write styles in response to viewport changes.\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n callbackRef();\n }\n });\n\n resizeObserver.observe(el);\n mutationObserver.observe(el, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n },\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;;AAIA,MAAaG,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAA;GACE,MAAAC,iBAAuB,IAAIC,qBAAe;IACxCJ,YAAY;GAAC,CACd;GAED,MAAAK,mBAAyB,IAAIC,kBAAiBC,cAAA;IAM5C,IAH4BA,UAASE,KACnCC,KAEEF,GACFR,YAAY;GACb,CACF;GAEDG,eAAcQ,QAAST,EAAE;GACzBG,iBAAgBM,QAAST,IAAI;IAAAU,WAChB;IAAIC,SACN;IAAIC,YACD;IAAIC,eACD;GACjB,CAAC;GAAC,aAEK;IACLZ,eAAca,WAAY;IAC1BX,iBAAgBW,WAAY;GAAC;EAC9B;EACFlB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAED,OAEKH,cAAcsB,EAAW;AAAC;AApCD,SAAAP,MAAAQ,GAAA;CAAA,OAajBA,EAACC,SAAU,gBAAgBD,EAACE,kBAAmB;AAAO"}
@@ -2,7 +2,7 @@
2
2
  import { useThreadViewport } from "../../context/react/ThreadViewportContext.js";
3
3
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
4
4
  import { useEffect } from "@assistant-ui/tap/react-shim";
5
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
5
+ import { useCallbackRef } from "radix-ui/internal";
6
6
  //#region src/utils/hooks/useOnScrollToBottom.ts
7
7
  const useOnScrollToBottom = (callback) => {
8
8
  const $ = c(4);
@@ -1 +1 @@
1
- {"version":3,"file":"useOnScrollToBottom.js","names":["c","_c","useCallbackRef","useEffect","useThreadViewport","useOnScrollToBottom","callback","$","callbackRef","onScrollToBottom","_temp","t0","t1","vp"],"sources":["../../../src/utils/hooks/useOnScrollToBottom.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useEffect } from \"react\";\nimport { useThreadViewport } from \"../../context/react/ThreadViewportContext\";\n\nexport const useOnScrollToBottom = (\n callback: (config: { behavior: ScrollBehavior }) => void,\n) => {\n const callbackRef = useCallbackRef(callback);\n const onScrollToBottom = useThreadViewport((vp) => vp.onScrollToBottom);\n\n useEffect(() => {\n return onScrollToBottom(callbackRef);\n }, [onScrollToBottom, callbackRef]);\n};\n"],"mappings":";;;;;;AAMA,MAAaK,uBAAsBC,aAAA;CAAA,MAAAC,IAAAN,EAAA,CAAA;CAGjC,MAAAO,cAAoBN,eAAeI,QAAQ;CAC3C,MAAAG,mBAAyBL,kBAAkBM,KAA2B;CAAE,IAAAC;CAAA,IAAAC;CAAA,IAAAL,EAAA,OAAAC,eAAAD,EAAA,OAAAE,kBAAA;EAE9DE,WACDF,iBAAiBD,WAAW;EAClCI,KAAA,CAACH,kBAAkBD,WAAW;EAACD,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;CAAA,OAAA;EAAAD,KAAAJ,EAAA;EAAAK,KAAAL,EAAA;CAAA;CAFlCJ,UAAUQ,IAEPC,EAA+B;AAAC;AARF,SAAAF,MAAAG,IAAA;CAAA,OAIkBA,GAAEJ;AAAiB"}
1
+ {"version":3,"file":"useOnScrollToBottom.js","names":["c","_c","useCallbackRef","useEffect","useThreadViewport","useOnScrollToBottom","callback","$","callbackRef","onScrollToBottom","_temp","t0","t1","vp"],"sources":["../../../src/utils/hooks/useOnScrollToBottom.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallbackRef } from \"radix-ui/internal\";\nimport { useEffect } from \"react\";\nimport { useThreadViewport } from \"../../context/react/ThreadViewportContext\";\n\nexport const useOnScrollToBottom = (\n callback: (config: { behavior: ScrollBehavior }) => void,\n) => {\n const callbackRef = useCallbackRef(callback);\n const onScrollToBottom = useThreadViewport((vp) => vp.onScrollToBottom);\n\n useEffect(() => {\n return onScrollToBottom(callbackRef);\n }, [onScrollToBottom, callbackRef]);\n};\n"],"mappings":";;;;;;AAMA,MAAaK,uBAAsBC,aAAA;CAAA,MAAAC,IAAAN,EAAA,CAAA;CAGjC,MAAAO,cAAoBN,eAAeI,QAAQ;CAC3C,MAAAG,mBAAyBL,kBAAkBM,KAA2B;CAAE,IAAAC;CAAA,IAAAC;CAAA,IAAAL,EAAA,OAAAC,eAAAD,EAAA,OAAAE,kBAAA;EAE9DE,WACDF,iBAAiBD,WAAW;EAClCI,KAAA,CAACH,kBAAkBD,WAAW;EAACD,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;CAAA,OAAA;EAAAD,KAAAJ,EAAA;EAAAK,KAAAL,EAAA;CAAA;CAFlCJ,UAAUQ,IAEPC,EAA+B;AAAC;AARF,SAAAF,MAAAG,IAAA;CAAA,OAIkBA,GAAEJ;AAAiB"}
@@ -15,6 +15,10 @@ declare const useSmoothStatus: {
15
15
  } | {
16
16
  readonly type: "requires-action";
17
17
  readonly reason: "tool-calls" | "interrupt";
18
+ } | {
19
+ readonly type: "incomplete";
20
+ readonly reason: "tool-calls";
21
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
18
22
  };
19
23
  <TSelected>(selector: (state: {
20
24
  readonly type: "running";
@@ -27,6 +31,10 @@ declare const useSmoothStatus: {
27
31
  } | {
28
32
  readonly type: "requires-action";
29
33
  readonly reason: "tool-calls" | "interrupt";
34
+ } | {
35
+ readonly type: "incomplete";
36
+ readonly reason: "tool-calls";
37
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
30
38
  }) => TSelected): TSelected;
31
39
  (options: {
32
40
  optional: true;
@@ -41,6 +49,10 @@ declare const useSmoothStatus: {
41
49
  } | {
42
50
  readonly type: "requires-action";
43
51
  readonly reason: "tool-calls" | "interrupt";
52
+ } | {
53
+ readonly type: "incomplete";
54
+ readonly reason: "tool-calls";
55
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
44
56
  } | null;
45
57
  <TSelected>(options: {
46
58
  optional: true;
@@ -55,6 +67,10 @@ declare const useSmoothStatus: {
55
67
  } | {
56
68
  readonly type: "requires-action";
57
69
  readonly reason: "tool-calls" | "interrupt";
70
+ } | {
71
+ readonly type: "incomplete";
72
+ readonly reason: "tool-calls";
73
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
58
74
  }) => TSelected;
59
75
  }): TSelected | null;
60
76
  }, useSmoothStatusStore: {
@@ -69,6 +85,10 @@ declare const useSmoothStatus: {
69
85
  } | {
70
86
  readonly type: "requires-action";
71
87
  readonly reason: "tool-calls" | "interrupt";
88
+ } | {
89
+ readonly type: "incomplete";
90
+ readonly reason: "tool-calls";
91
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
72
92
  }>;
73
93
  (options: {
74
94
  optional: true;
@@ -83,6 +103,10 @@ declare const useSmoothStatus: {
83
103
  } | {
84
104
  readonly type: "requires-action";
85
105
  readonly reason: "tool-calls" | "interrupt";
106
+ } | {
107
+ readonly type: "incomplete";
108
+ readonly reason: "tool-calls";
109
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
86
110
  }> | null;
87
111
  };
88
112
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"SmoothContext.d.ts","names":[],"sources":["../../../src/utils/smooth/SmoothContext.tsx"],"mappings":";;;cAmCa,uBAAuB,GAAG;cAgB1B,4BAA6B,UAAU,oBAClD,WAAW,MACV;cA2BY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiB"}
1
+ {"version":3,"file":"SmoothContext.d.ts","names":[],"sources":["../../../src/utils/smooth/SmoothContext.tsx"],"mappings":";;;cAmCa,uBAAuB,GAAG;cAgB1B,4BAA6B,UAAU,oBAClD,WAAW,MACV;cA2BY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiB"}
@@ -4,7 +4,7 @@ import { useMediaQuery } from "../hooks/useMediaQuery.js";
4
4
  import { useSmoothStatusStore } from "./SmoothContext.js";
5
5
  import { useAui, useAuiState } from "@assistant-ui/store";
6
6
  import { useEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
7
- import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
7
+ import { useCallbackRef } from "radix-ui/internal";
8
8
  //#region src/utils/smooth/useSmooth.ts
9
9
  const DEFAULT_DRAIN_MS = 250;
10
10
  const DEFAULT_MAX_CHAR_INTERVAL_MS = 5;
@@ -1 +1 @@
1
- {"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useMediaQuery","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","minCommitMs","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","lastCommitTime","targetText","Infinity","currentText","setText","newText","constructor","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","isComplete","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","reduceMotion","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useMediaQuery } from \"../hooks/useMediaQuery\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n /**\n * Minimum time in milliseconds between committed updates. The reveal keeps\n * advancing every frame, but the visible text (and the downstream re-render\n * and markdown re-parse it triggers) is committed at most once per interval.\n * The final frame always commits. `0` commits every frame.\n * @default 0\n */\n minCommitMs?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n public lastCommitTime: number = 0;\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\n public minCommitMs: number = 0;\n\n public currentText: string;\n private setText: (newText: string) => void;\n\n constructor(currentText: string, setText: (newText: string) => void) {\n this.currentText = currentText;\n this.setText = setText;\n }\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n\n const isComplete = charsToAdd === remainingChars;\n if (isComplete || currentTime - this.lastCommitTime >= this.minCommitMs) {\n this.lastCommitTime = currentTime;\n this.setText(this.currentText);\n }\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n * If the source settles before any character has been revealed, the text\n * is committed immediately so a missed animation frame cannot leave an\n * empty bubble.\n *\n * The reveal auto-disables under `prefers-reduced-motion: reduce`,\n * committing the full text immediately; this takes precedence over an\n * explicit `smooth={true}`.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const reduceMotion = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null && !reduceMotion;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n const minCommitMs = positiveOr(options?.minCommitMs, 0);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part);\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n animatorRef.minCommitMs = minCommitMs;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame, minCommitMs]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n // A new part whose text shares a prefix with the previous target would\n // keep the stale cursor and flicker without this reset.\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.lastCommitTime = 0;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n setText(text);\n }\n return;\n }\n\n animatorRef.targetText = text;\n if (state.status.type !== \"running\") {\n // No character has painted. A pending frame that never runs would\n // leave an empty bubble after settle.\n if (animatorRef.currentText === \"\") {\n animatorRef.currentText = text;\n animatorRef.stop();\n setText(text);\n return;\n }\n animatorRef.start();\n return;\n }\n\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part, setText]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useMediaQuery","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","minCommitMs","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","lastCommitTime","targetText","Infinity","currentText","setText","newText","constructor","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","isComplete","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","reduceMotion","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"radix-ui/internal\";\nimport { useMediaQuery } from \"../hooks/useMediaQuery\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n /**\n * Minimum time in milliseconds between committed updates. The reveal keeps\n * advancing every frame, but the visible text (and the downstream re-render\n * and markdown re-parse it triggers) is committed at most once per interval.\n * The final frame always commits. `0` commits every frame.\n * @default 0\n */\n minCommitMs?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n public lastCommitTime: number = 0;\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\n public minCommitMs: number = 0;\n\n public currentText: string;\n private setText: (newText: string) => void;\n\n constructor(currentText: string, setText: (newText: string) => void) {\n this.currentText = currentText;\n this.setText = setText;\n }\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n\n const isComplete = charsToAdd === remainingChars;\n if (isComplete || currentTime - this.lastCommitTime >= this.minCommitMs) {\n this.lastCommitTime = currentTime;\n this.setText(this.currentText);\n }\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n * If the source settles before any character has been revealed, the text\n * is committed immediately so a missed animation frame cannot leave an\n * empty bubble.\n *\n * The reveal auto-disables under `prefers-reduced-motion: reduce`,\n * committing the full text immediately; this takes precedence over an\n * explicit `smooth={true}`.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const reduceMotion = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null && !reduceMotion;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n const minCommitMs = positiveOr(options?.minCommitMs, 0);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part);\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n animatorRef.minCommitMs = minCommitMs;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame, minCommitMs]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n // A new part whose text shares a prefix with the previous target would\n // keep the stale cursor and flicker without this reset.\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.lastCommitTime = 0;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n setText(text);\n }\n return;\n }\n\n animatorRef.targetText = text;\n if (state.status.type !== \"running\") {\n // No character has painted. A pending frame that never runs would\n // leave an empty bubble after settle.\n if (animatorRef.currentText === \"\") {\n animatorRef.currentText = text;\n animatorRef.stop();\n setText(text);\n return;\n }\n animatorRef.start();\n return;\n }\n\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part, setText]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  //#region src/utils/useToolArgsFieldStatus.d.ts
2
2
  declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
3
+ type: string;
4
+ } | {
3
5
  readonly type: "running";
4
6
  } | {
5
7
  readonly type: "complete";
@@ -8,7 +10,9 @@ declare const useToolArgsFieldStatus: (fieldPath: (string | number)[]) => {
8
10
  readonly reason: "cancelled" | "length" | "content-filter" | "other" | "error";
9
11
  readonly error?: unknown;
10
12
  } | {
11
- type: string;
13
+ readonly type: "incomplete";
14
+ readonly reason: "tool-calls";
15
+ readonly error?: import("assistant-stream/utils").ReadonlyJSONValue;
12
16
  };
13
17
  //#endregion
14
18
  export { useToolArgsFieldStatus };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.15.16",
3
+ "version": "0.15.17",
4
4
  "description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
5
5
  "keywords": [
6
6
  "ai",
@@ -55,22 +55,14 @@
55
55
  ],
56
56
  "sideEffects": false,
57
57
  "dependencies": {
58
- "@assistant-ui/core": "^0.3.15",
59
- "@assistant-ui/store": "^0.3.10",
60
- "@assistant-ui/tap": "^0.9.14",
61
- "@radix-ui/primitive": "^1.1.7",
62
- "@radix-ui/react-collection": "^1.1.15",
63
- "@radix-ui/react-compose-refs": "^1.1.5",
64
- "@radix-ui/react-context": "^1.2.2",
65
- "@radix-ui/react-primitive": "^2.1.10",
66
- "@radix-ui/react-use-callback-ref": "^1.1.4",
67
- "@radix-ui/react-use-controllable-state": "^1.2.6",
68
- "@radix-ui/react-use-escape-keydown": "^1.1.5",
69
- "assistant-cloud": "^0.1.41",
70
- "assistant-stream": "^0.3.39",
58
+ "@assistant-ui/core": "^0.3.16",
59
+ "@assistant-ui/store": "^0.3.11",
60
+ "@assistant-ui/tap": "^0.9.15",
61
+ "assistant-cloud": "^0.1.42",
62
+ "assistant-stream": "^0.3.40",
71
63
  "radix-ui": "^1.6.7",
72
64
  "react-textarea-autosize": "^8.5.9",
73
- "safe-content-frame": "^0.0.27",
65
+ "safe-content-frame": "^0.0.28",
74
66
  "zod": "^4.4.3",
75
67
  "zustand": "^5.0.15"
76
68
  },
@@ -89,17 +81,17 @@
89
81
  }
90
82
  },
91
83
  "devDependencies": {
84
+ "@assistant-ui/vite": "0.0.15",
85
+ "@assistant-ui/x-buildutils": "0.0.24",
92
86
  "@testing-library/react": "^16.3.2",
93
87
  "@types/json-schema": "^7.0.15",
94
88
  "@types/node": "^26.2.0",
95
89
  "@types/react": "^19.2.18",
96
- "@types/react-dom": "^19.2.4",
90
+ "@types/react-dom": "^19.2.5",
97
91
  "jsdom": "^30.0.1",
98
92
  "react": "^19.2.8",
99
93
  "react-dom": "^19.2.8",
100
- "vitest": "^4.1.10",
101
- "@assistant-ui/vite": "0.0.14",
102
- "@assistant-ui/x-buildutils": "0.0.23"
94
+ "vitest": "^4.1.11"
103
95
  },
104
96
  "publishConfig": {
105
97
  "access": "public",
package/src/index.ts CHANGED
@@ -83,7 +83,12 @@ export {
83
83
  } from "@assistant-ui/core";
84
84
 
85
85
  // --- adapters/suggestion ---
86
- export type { SuggestionAdapter } from "@assistant-ui/core";
86
+ export type {
87
+ SuggestionAdapter,
88
+ SuggestionAdapterGenerateOptions,
89
+ CreateSuggestionAdapterOptions,
90
+ } from "@assistant-ui/core";
91
+ export { createSuggestionAdapter } from "@assistant-ui/core";
87
92
 
88
93
  // --- adapters/RuntimeAdapterProvider ---
89
94
  export {
@@ -475,6 +480,8 @@ export {
475
480
  useTriggerPopoverTriggers as unstable_useTriggerPopoverTriggers,
476
481
  useTriggerPopoverTriggersOptional as unstable_useTriggerPopoverTriggersOptional,
477
482
  type RegisteredTrigger as Unstable_RegisteredTrigger,
483
+ type TriggerMatch as Unstable_TriggerMatch,
484
+ type TriggerMatcher as Unstable_TriggerMatcher,
478
485
  type TriggerBehavior as Unstable_TriggerBehavior,
479
486
  } from "./primitives/composer/trigger";
480
487
  export type {
@@ -520,6 +527,7 @@ export type {
520
527
  McpAppsHost,
521
528
  McpAppsRemoteHostOptions,
522
529
  McpAppToolCallParams,
530
+ McpAppBridgeHandlers,
523
531
  ToolCallMessagePartMcpMetadata,
524
532
  } from "./mcp-apps";
525
533
  export type { McpAppResourceOutput } from "@assistant-ui/core/react";
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment jsdom
2
2
  import { render, waitFor } from "@testing-library/react";
3
3
  import { resource, useResource, withKey } from "@assistant-ui/tap";
4
- import { memo } from "react";
4
+ import { act, memo, startTransition, Suspense } from "react";
5
5
  import type {
6
6
  ToolCallMessagePartComponent,
7
7
  ToolCallMessagePartProps,
@@ -29,7 +29,7 @@ vi.mock("./app-frame", () => ({
29
29
  },
30
30
  }));
31
31
 
32
- import { McpAppRenderer } from "./McpAppRenderer";
32
+ import { McpAppRenderer, type McpAppRendererOptions } from "./McpAppRenderer";
33
33
  import { McpAppsRemoteHost } from "./McpAppsRemoteHost";
34
34
 
35
35
  const useHost = ({ host }: { host: McpAppsHost }) => host;
@@ -61,14 +61,31 @@ const createPart = (serverId?: string): ToolCallMessagePartProps => ({
61
61
  respondToApproval: vi.fn(),
62
62
  });
63
63
 
64
- function Harness({ host, serverId }: { host: McpAppsHost; serverId?: string }) {
64
+ const createPartWithoutApp = (): ToolCallMessagePartProps => {
65
+ const part = createPart();
66
+ delete part.mcp;
67
+ return part;
68
+ };
69
+
70
+ function Harness({
71
+ host,
72
+ serverId,
73
+ handlers,
74
+ part,
75
+ }: {
76
+ host: McpAppsHost;
77
+ serverId?: string;
78
+ handlers?: McpAppRendererOptions["handlers"];
79
+ part?: ToolCallMessagePartProps;
80
+ }) {
65
81
  const renderer = useResource(
66
82
  McpAppRenderer({
67
83
  host: Host({ host }),
84
+ ...(handlers === undefined ? {} : { handlers }),
68
85
  }),
69
86
  );
70
87
  const Renderer = renderer.render;
71
- return <Renderer {...createPart(serverId)} />;
88
+ return <Renderer {...(part ?? createPart(serverId))} />;
72
89
  }
73
90
 
74
91
  const MemoizedPart = memo(function MemoizedPart({
@@ -142,6 +159,64 @@ describe("McpAppRenderer", () => {
142
159
  });
143
160
  });
144
161
 
162
+ it("keeps bridge server ids scoped to committed renders", async () => {
163
+ const host: McpAppsHost = {
164
+ loadResource: vi.fn(async ({ uri }) => ({
165
+ uri,
166
+ mimeType: "text/html;profile=mcp-app" as const,
167
+ html: "",
168
+ })),
169
+ callTool: vi.fn(),
170
+ readResource: vi.fn(),
171
+ listResources: vi.fn(),
172
+ };
173
+ const interruptedRender = vi.fn();
174
+ const pending = new Promise<never>(() => {});
175
+ const Blocker = ({ blocked }: { blocked: boolean }) => {
176
+ if (blocked) {
177
+ interruptedRender();
178
+ throw pending;
179
+ }
180
+ return null;
181
+ };
182
+ const view = (part: ToolCallMessagePartProps, blocked: boolean) => (
183
+ <Suspense fallback={null}>
184
+ <Harness host={host} part={part} />
185
+ <Blocker blocked={blocked} />
186
+ </Suspense>
187
+ );
188
+ const rendered = render(view(createPart("server-a"), false));
189
+ await waitFor(() => expect(framePropsMock).toHaveBeenCalled());
190
+ const handlers = framePropsMock.mock.lastCall?.[0]
191
+ .handlers as McpAppBridgeHandlers;
192
+
193
+ act(() => {
194
+ startTransition(() =>
195
+ rendered.rerender(view(createPart("server-b"), true)),
196
+ );
197
+ });
198
+ expect(interruptedRender).toHaveBeenCalled();
199
+ await handlers.callTool?.({ name: "search" });
200
+
201
+ expect(host.callTool).toHaveBeenCalledWith({
202
+ name: "search",
203
+ serverId: "server-a",
204
+ });
205
+
206
+ framePropsMock.mockClear();
207
+ vi.mocked(host.callTool).mockClear();
208
+ rendered.rerender(view(createPartWithoutApp(), false));
209
+ await waitFor(() => expect(framePropsMock).toHaveBeenCalled());
210
+ expect(framePropsMock.mock.lastCall?.[0].app.serverId).toBe("server-a");
211
+ const fallbackHandlers = framePropsMock.mock.lastCall?.[0]
212
+ .handlers as McpAppBridgeHandlers;
213
+ await fallbackHandlers.callTool?.({ name: "search" });
214
+ expect(host.callTool).toHaveBeenCalledWith({
215
+ name: "search",
216
+ serverId: "server-a",
217
+ });
218
+ });
219
+
145
220
  it("reloads the resource and hides stale HTML when the host changes", async () => {
146
221
  const nextResource = createDeferred<{
147
222
  uri: string;
@@ -397,4 +472,58 @@ describe("McpAppRenderer", () => {
397
472
  await handlers.listResources?.(params);
398
473
  expect(listResources).toHaveBeenCalledWith(params);
399
474
  });
475
+
476
+ it("uses caller UI handlers and keeps data-plane handlers on the host", async () => {
477
+ const host: McpAppsHost = {
478
+ loadResource: vi.fn(async ({ uri }) => ({
479
+ uri,
480
+ mimeType: "text/html;profile=mcp-app" as const,
481
+ html: "",
482
+ })),
483
+ callTool: vi.fn(),
484
+ readResource: vi.fn(),
485
+ listResources: vi.fn(),
486
+ };
487
+ const requestDisplayMode = vi.fn(({ mode }) => ({ mode }));
488
+ const updateModelContext = vi.fn();
489
+ const openLink = vi.fn();
490
+ const sendMessage = vi.fn();
491
+ const onInitialized = vi.fn();
492
+
493
+ render(
494
+ <Harness
495
+ host={host}
496
+ handlers={{
497
+ requestDisplayMode,
498
+ updateModelContext,
499
+ openLink,
500
+ sendMessage,
501
+ onInitialized,
502
+ }}
503
+ />,
504
+ );
505
+ await waitFor(() => expect(framePropsMock).toHaveBeenCalled());
506
+ const handlers = framePropsMock.mock.lastCall?.[0]
507
+ .handlers as McpAppBridgeHandlers;
508
+
509
+ expect(await handlers.requestDisplayMode?.({ mode: "fullscreen" })).toEqual(
510
+ { mode: "fullscreen" },
511
+ );
512
+ await handlers.updateModelContext?.({ text: "context" });
513
+ await handlers.openLink?.({ url: "https://example.com" });
514
+ await handlers.sendMessage?.({ text: "hello" });
515
+ handlers.onInitialized?.();
516
+ await handlers.callTool?.({ name: "search" });
517
+ await handlers.readResource?.({ uri: "ui://resource" });
518
+ await handlers.listResources?.();
519
+
520
+ expect(requestDisplayMode).toHaveBeenCalledWith({ mode: "fullscreen" });
521
+ expect(updateModelContext).toHaveBeenCalledWith({ text: "context" });
522
+ expect(openLink).toHaveBeenCalledWith({ url: "https://example.com" });
523
+ expect(sendMessage).toHaveBeenCalledWith({ text: "hello" });
524
+ expect(onInitialized).toHaveBeenCalledOnce();
525
+ expect(host.callTool).toHaveBeenCalledWith({ name: "search" });
526
+ expect(host.readResource).toHaveBeenCalledWith({ uri: "ui://resource" });
527
+ expect(host.listResources).toHaveBeenCalledWith(undefined);
528
+ });
400
529
  });