@assistant-ui/react 0.14.14 → 0.14.16

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 (207) hide show
  1. package/README.md +5 -1
  2. package/dist/client/ExternalThread.d.ts +2 -12
  3. package/dist/client/ExternalThread.d.ts.map +1 -1
  4. package/dist/client/ExternalThread.js +30 -29
  5. package/dist/client/ExternalThread.js.map +1 -1
  6. package/dist/client/InMemoryThreadList.d.ts.map +1 -1
  7. package/dist/client/InMemoryThreadList.js +11 -10
  8. package/dist/client/InMemoryThreadList.js.map +1 -1
  9. package/dist/client/SingleThreadList.d.ts.map +1 -1
  10. package/dist/client/SingleThreadList.js +9 -8
  11. package/dist/client/SingleThreadList.js.map +1 -1
  12. package/dist/context/providers/ThreadViewportProvider.js +1 -1
  13. package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
  14. package/dist/context/react/ThreadViewportContext.js +1 -1
  15. package/dist/context/react/utils/createContextHook.js +1 -1
  16. package/dist/context/react/utils/ensureBinding.js.map +1 -1
  17. package/dist/context/react/utils/useRuntimeState.js +1 -1
  18. package/dist/context/stores/ThreadViewport.js.map +1 -1
  19. package/dist/devtools/DevToolsHooks.js.map +1 -1
  20. package/dist/index.d.ts +4 -4
  21. package/dist/index.js +3 -3
  22. package/dist/legacy-runtime/AssistantRuntimeProvider.js +1 -1
  23. package/dist/legacy-runtime/cloud/auiV0.js +1 -1
  24. package/dist/legacy-runtime/hooks/AssistantContext.js.map +1 -1
  25. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +1 -1
  26. package/dist/legacy-runtime/hooks/ComposerContext.js.map +1 -1
  27. package/dist/legacy-runtime/hooks/MessageContext.js.map +1 -1
  28. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +1 -1
  29. package/dist/legacy-runtime/hooks/ThreadContext.js +1 -1
  30. package/dist/legacy-runtime/hooks/ThreadContext.js.map +1 -1
  31. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +1 -1
  32. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +1 -1
  33. package/dist/legacy-runtime/runtime-cores/assistant-transport/replayBoundaryStream.js +1 -1
  34. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +1 -1
  35. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +1 -1
  36. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  37. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -1
  38. package/dist/legacy-runtime/runtime-cores/assistant-transport/useLatestRef.js +1 -1
  39. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  40. package/dist/mcp-apps/McpAppRenderer.js +7 -7
  41. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  42. package/dist/mcp-apps/McpAppsRemoteHost.d.ts.map +1 -1
  43. package/dist/mcp-apps/McpAppsRemoteHost.js +5 -4
  44. package/dist/mcp-apps/McpAppsRemoteHost.js.map +1 -1
  45. package/dist/mcp-apps/app-frame.d.ts +1 -1
  46. package/dist/mcp-apps/app-frame.d.ts.map +1 -1
  47. package/dist/mcp-apps/app-frame.js +82 -104
  48. package/dist/mcp-apps/app-frame.js.map +1 -1
  49. package/dist/mcp-apps/bridge.d.ts +3 -3
  50. package/dist/mcp-apps/bridge.d.ts.map +1 -1
  51. package/dist/mcp-apps/bridge.js +35 -10
  52. package/dist/mcp-apps/bridge.js.map +1 -1
  53. package/dist/mcp-apps/types.d.ts +2 -12
  54. package/dist/mcp-apps/types.d.ts.map +1 -1
  55. package/dist/mcp-apps/types.js.map +1 -1
  56. package/dist/model-context/frame/useAssistantFrameHost.js +1 -1
  57. package/dist/model-context/makeAssistantVisible.js +1 -1
  58. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  59. package/dist/primitives/actionBar/ActionBarCopy.js +1 -1
  60. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +1 -1
  61. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  62. package/dist/primitives/actionBar/ActionBarFeedbackNegative.js +1 -1
  63. package/dist/primitives/actionBar/ActionBarFeedbackPositive.js +1 -1
  64. package/dist/primitives/actionBar/ActionBarInteractionContext.js +1 -1
  65. package/dist/primitives/actionBar/ActionBarRoot.js +1 -1
  66. package/dist/primitives/actionBar/ActionBarStopSpeaking.js +1 -1
  67. package/dist/primitives/actionBarMore/ActionBarMoreContent.js +1 -1
  68. package/dist/primitives/actionBarMore/ActionBarMoreItem.js +1 -1
  69. package/dist/primitives/actionBarMore/ActionBarMoreRoot.js +1 -1
  70. package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js +1 -1
  71. package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js +1 -1
  72. package/dist/primitives/assistantModal/AssistantModalAnchor.js +1 -1
  73. package/dist/primitives/assistantModal/AssistantModalContent.js +1 -1
  74. package/dist/primitives/assistantModal/AssistantModalRoot.js +1 -1
  75. package/dist/primitives/assistantModal/AssistantModalTrigger.js +1 -1
  76. package/dist/primitives/attachment/AttachmentRemove.js +1 -1
  77. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  78. package/dist/primitives/attachment/AttachmentRoot.js +1 -1
  79. package/dist/primitives/attachment/AttachmentThumb.js +1 -1
  80. package/dist/primitives/branchPicker/BranchPickerRoot.js +1 -1
  81. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +1 -1
  82. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  83. package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js +1 -1
  84. package/dist/primitives/composer/ComposerAddAttachment.js +1 -1
  85. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  86. package/dist/primitives/composer/ComposerAttachmentDropzone.js +1 -1
  87. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  88. package/dist/primitives/composer/ComposerDictationTranscript.js +1 -1
  89. package/dist/primitives/composer/ComposerInput.js +1 -1
  90. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  91. package/dist/primitives/composer/ComposerInputPluginContext.js +1 -1
  92. package/dist/primitives/composer/ComposerQuote.js +1 -1
  93. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  94. package/dist/primitives/composer/ComposerRoot.js +1 -1
  95. package/dist/primitives/composer/ComposerSend.js +1 -1
  96. package/dist/primitives/composer/ComposerStopDictation.js +1 -1
  97. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  98. package/dist/primitives/composer/trigger/TriggerPopover.js +2 -2
  99. package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
  100. package/dist/primitives/composer/trigger/TriggerPopoverAction.js +1 -1
  101. package/dist/primitives/composer/trigger/TriggerPopoverBack.js +1 -1
  102. package/dist/primitives/composer/trigger/TriggerPopoverCategories.js +1 -1
  103. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +1 -1
  104. package/dist/primitives/composer/trigger/TriggerPopoverItems.js +1 -1
  105. package/dist/primitives/composer/trigger/TriggerPopoverResource.d.ts.map +1 -1
  106. package/dist/primitives/composer/trigger/TriggerPopoverResource.js +8 -7
  107. package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
  108. package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js +1 -1
  109. package/dist/primitives/composer/trigger/triggerDetectionResource.d.ts.map +1 -1
  110. package/dist/primitives/composer/trigger/triggerDetectionResource.js +5 -4
  111. package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
  112. package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
  113. package/dist/primitives/composer/trigger/triggerKeyboardResource.js +8 -7
  114. package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
  115. package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
  116. package/dist/primitives/composer/trigger/triggerNavigationResource.js +13 -12
  117. package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
  118. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  119. package/dist/primitives/composer/trigger/triggerSelectionResource.js +7 -6
  120. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  121. package/dist/primitives/error/ErrorMessage.js +1 -1
  122. package/dist/primitives/error/ErrorRoot.js +1 -1
  123. package/dist/primitives/message/MessagePartsGrouped.js +1 -1
  124. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  125. package/dist/primitives/message/MessageRoot.js +1 -1
  126. package/dist/primitives/message/MessageRoot.js.map +1 -1
  127. package/dist/primitives/messagePart/MessagePartImage.js +1 -1
  128. package/dist/primitives/messagePart/MessagePartText.js +1 -1
  129. package/dist/primitives/queueItem/QueueItemRemove.js +1 -1
  130. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  131. package/dist/primitives/queueItem/QueueItemSteer.js +1 -1
  132. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  133. package/dist/primitives/queueItem/QueueItemText.js +1 -1
  134. package/dist/primitives/reasoning/useScrollLock.js +1 -1
  135. package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
  136. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
  137. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  138. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +1 -1
  139. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  140. package/dist/primitives/suggestion/SuggestionDescription.js +1 -1
  141. package/dist/primitives/suggestion/SuggestionTitle.js +1 -1
  142. package/dist/primitives/suggestion/SuggestionTrigger.js +1 -1
  143. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  144. package/dist/primitives/thread/ThreadRoot.js +1 -1
  145. package/dist/primitives/thread/ThreadScrollToBottom.js +1 -1
  146. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
  147. package/dist/primitives/thread/ThreadViewport.js +1 -1
  148. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  149. package/dist/primitives/thread/ThreadViewportFooter.js +1 -1
  150. package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
  151. package/dist/primitives/thread/topAnchor/topAnchorTurn.js.map +1 -1
  152. package/dist/primitives/thread/topAnchor/topAnchorUtils.js.map +1 -1
  153. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +1 -1
  154. package/dist/primitives/thread/useThreadViewportAutoScroll.js +1 -1
  155. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  156. package/dist/primitives/threadList/ThreadListNew.js +1 -1
  157. package/dist/primitives/threadList/ThreadListRoot.js +1 -1
  158. package/dist/primitives/threadListItem/ThreadListItemRoot.js +1 -1
  159. package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js +1 -1
  160. package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js +1 -1
  161. package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js +1 -1
  162. package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js +1 -1
  163. package/dist/sandbox-host/SandboxHost.d.ts +50 -0
  164. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -0
  165. package/dist/sandbox-host/SandboxHost.js +85 -0
  166. package/dist/sandbox-host/SandboxHost.js.map +1 -0
  167. package/dist/unstable/useMentionAdapter.js +1 -1
  168. package/dist/unstable/useMentionAdapter.js.map +1 -1
  169. package/dist/unstable/useSlashCommandAdapter.js +1 -1
  170. package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
  171. package/dist/utils/Primitive.js +1 -1
  172. package/dist/utils/createActionButton.js +1 -1
  173. package/dist/utils/createActionButton.js.map +1 -1
  174. package/dist/utils/hooks/useManagedRef.js +1 -1
  175. package/dist/utils/hooks/useMediaQuery.js +1 -1
  176. package/dist/utils/hooks/useMediaQuery.js.map +1 -1
  177. package/dist/utils/hooks/useOnResizeContent.js +1 -1
  178. package/dist/utils/hooks/useOnScrollToBottom.js +1 -1
  179. package/dist/utils/hooks/useSizeHandle.js +1 -1
  180. package/dist/utils/json/is-json.js.map +1 -1
  181. package/dist/utils/smooth/SmoothContext.js +1 -1
  182. package/dist/utils/smooth/SmoothContext.js.map +1 -1
  183. package/dist/utils/smooth/useSmooth.js +1 -1
  184. package/dist/utils/smooth/useSmooth.js.map +1 -1
  185. package/dist/utils/useToolArgsFieldStatus.d.ts +2 -2
  186. package/dist/utils/useToolArgsFieldStatus.d.ts.map +1 -1
  187. package/package.json +48 -40
  188. package/src/client/ExternalThread.ts +484 -515
  189. package/src/client/InMemoryThreadList.ts +153 -162
  190. package/src/client/SingleThreadList.ts +87 -84
  191. package/src/context/providers/ThreadViewportProvider.tsx +2 -2
  192. package/src/index.ts +8 -1
  193. package/src/mcp-apps/McpAppRenderer.tsx +28 -35
  194. package/src/mcp-apps/McpAppsRemoteHost.ts +25 -24
  195. package/src/mcp-apps/app-frame.tsx +100 -141
  196. package/src/mcp-apps/bridge.test.ts +100 -60
  197. package/src/mcp-apps/bridge.ts +43 -21
  198. package/src/mcp-apps/types.ts +2 -12
  199. package/src/primitives/composer/trigger/TriggerPopover.tsx +1 -1
  200. package/src/primitives/composer/trigger/TriggerPopoverResource.ts +75 -76
  201. package/src/primitives/composer/trigger/triggerDetectionResource.ts +6 -5
  202. package/src/primitives/composer/trigger/triggerKeyboardResource.ts +9 -13
  203. package/src/primitives/composer/trigger/triggerNavigationResource.ts +14 -19
  204. package/src/primitives/composer/trigger/triggerSelectionResource.ts +8 -7
  205. package/src/sandbox-host/SandboxHost.test.tsx +231 -0
  206. package/src/sandbox-host/SandboxHost.tsx +185 -0
  207. package/src/tests/local-runtime-queue.test.tsx +305 -0
@@ -1 +1 @@
1
- {"version":3,"file":"MessagePartsGrouped.js","names":[],"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n type PropsWithChildren,\n useMemo,\n} from \"react\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\nimport { PartByIndexProvider } from \"../../context/providers/PartByIndexProvider\";\nimport { TextMessagePartProvider } from \"../../context/providers/TextMessagePartProvider\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n DataMessagePartComponent,\n DataMessagePartProps,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"@assistant-ui/core/react\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport type { MessagePartStatus } from \"@assistant-ui/core\";\n\ntype MessagePartGroup = {\n groupKey: string | undefined;\n indices: number[];\n};\n\nexport type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId: GroupingFunction = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const groupKey = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ groupKey, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGrouped = (\n groupingFunction: GroupingFunction,\n): MessagePartGroup[] => {\n const parts = useAuiState((s) => s.message.parts);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupingFunction(parts);\n }, [parts, groupingFunction]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGrouped {\n export type Props = {\n /**\n * Function that takes an array of message parts and returns an array of groups.\n * Each group contains a key (for identification) and an array of indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * groupingFunction={(parts) => {\n * const groups = new Map<string, number[]>();\n * parts.forEach((part, i) => {\n * const key = part.parentId ?? `__ungrouped_${i}`;\n * const indices = groups.get(key) ?? [];\n * indices.push(i);\n * groups.set(key, indices);\n * });\n * return Array.from(groups.entries()).map(([key, indices]) => ({\n * key: key.startsWith(\"__ungrouped_\") ? undefined : key,\n * indices\n * }));\n * }}\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ key, indices, children }) => {\n * if (!key) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {key}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\n groupingFunction: GroupingFunction;\n\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for data part rendering */\n data?:\n | {\n /** Map data event names to specific components */\n by_name?:\n | Record<string, DataMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unmatched data events */\n Fallback?: DataMessagePartComponent | undefined;\n }\n | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same group key as determined by the groupingFunction.\n *\n * The component receives:\n * - `groupKey`: The group key (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible group\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <details className=\"message-group\">\n * <summary>\n * Group {groupKey} ({indices.length} parts)\n * </summary>\n * <div className=\"group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @param groupKey - The group key (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n groupKey: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.tools.tools[props.toolName] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst DataUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: DataMessagePartComponent | undefined;\n} & DataMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.dataRenderers.renderers[props.name] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n data,\n } = {},\n}) => {\n const aui = useAui();\n const part = useAuiState((s) => s.part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = aui.part().addToolResult;\n const resume = aui.part().resumeToolCall;\n const respondToApproval = aui.part().respondToToolApproval;\n if (\"Override\" in tools)\n return (\n <tools.Override\n {...part}\n addResult={addResult}\n resume={resume}\n respondToApproval={respondToApproval}\n />\n );\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n respondToApproval={respondToApproval}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n case \"data\": {\n const Data = data?.by_name?.[part.name] ?? data?.Fallback;\n return <DataUIDisplay {...part} Fallback={Data} />;\n }\n\n default:\n console.warn(`Unknown message part type: ${type}`);\n return null;\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n return (\n <PartByIndexProvider index={partIndex}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.data === next.components?.data &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAuiState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by a custom grouping function.\n *\n * This component allows you to group message parts based on any criteria you define.\n * The grouping function receives all message parts and returns an array of groups,\n * where each group has a key and an array of part indices.\n *\n * @deprecated Prefer `<MessagePrimitive.GroupedParts>` for adjacent\n * grouping — it dispatches all rendering through one `switch (part.type)`\n * and supports nested group paths. Keep this primitive only for\n * non-adjacent clustering (e.g., gathering parts with the same parent-id\n * across the message).\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * <MessagePrimitive.Unstable_PartsGrouped\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Parent ID: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGrouped: FC<\n MessagePrimitiveUnstable_PartsGrouped.Props\n> = ({ groupingFunction, components }) => {\n const contentLength = useAuiState((s) => s.message.parts.length);\n const messageGroups = useMessagePartsGrouped(groupingFunction);\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.groupKey ?? \"ungrouped\"}`}\n groupKey={group.groupKey}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGrouped.displayName =\n \"MessagePrimitive.Unstable_PartsGrouped\";\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.\n *\n * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n Omit<MessagePrimitiveUnstable_PartsGrouped.Props, \"groupingFunction\">\n> = ({ components, ...props }) => {\n return (\n <MessagePrimitiveUnstable_PartsGrouped\n {...props}\n components={components}\n groupingFunction={groupMessagePartsByParentId}\n />\n );\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessagePartsGrouped.js","names":[],"sources":["../../../src/primitives/message/MessagePartsGrouped.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n type PropsWithChildren,\n useMemo,\n} from \"react\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\nimport { PartByIndexProvider } from \"../../context/providers/PartByIndexProvider\";\nimport { TextMessagePartProvider } from \"../../context/providers/TextMessagePartProvider\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n DataMessagePartComponent,\n DataMessagePartProps,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"@assistant-ui/core/react\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport type { MessagePartStatus } from \"@assistant-ui/core\";\n\ntype MessagePartGroup = {\n groupKey: string | undefined;\n indices: number[];\n};\n\nexport type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId: GroupingFunction = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const groupKey = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ groupKey, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGrouped = (\n groupingFunction: GroupingFunction,\n): MessagePartGroup[] => {\n const parts = useAuiState((s) => s.message.parts);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupingFunction(parts);\n }, [parts, groupingFunction]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGrouped {\n export type Props = {\n /**\n * Function that takes an array of message parts and returns an array of groups.\n * Each group contains a key (for identification) and an array of indices.\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * groupingFunction={(parts) => {\n * const groups = new Map<string, number[]>();\n * parts.forEach((part, i) => {\n * const key = part.parentId ?? `__ungrouped_${i}`;\n * const indices = groups.get(key) ?? [];\n * indices.push(i);\n * groups.set(key, indices);\n * });\n * return Array.from(groups.entries()).map(([key, indices]) => ({\n * key: key.startsWith(\"__ungrouped_\") ? undefined : key,\n * indices\n * }));\n * }}\n * ```\n *\n * @example\n * ```tsx\n * // Group by tool name\n * import { groupMessagePartsByToolName } from \"@assistant-ui/react\";\n *\n * <MessagePrimitive.Unstable_PartsGrouped\n * groupingFunction={groupMessagePartsByToolName}\n * components={{\n * Group: ({ key, indices, children }) => {\n * if (!key) return <>{children}</>;\n * return (\n * <div className=\"tool-group\">\n * <h4>Tool: {key}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\n groupingFunction: GroupingFunction;\n\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for data part rendering */\n data?:\n | {\n /** Map data event names to specific components */\n by_name?:\n | Record<string, DataMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unmatched data events */\n Fallback?: DataMessagePartComponent | undefined;\n }\n | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same group key as determined by the groupingFunction.\n *\n * The component receives:\n * - `groupKey`: The group key (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible group\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <details className=\"message-group\">\n * <summary>\n * Group {groupKey} ({indices.length} parts)\n * </summary>\n * <div className=\"group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @param groupKey - The group key (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n groupKey: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.tools.tools[props.toolName] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst DataUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: DataMessagePartComponent | undefined;\n} & DataMessagePartProps) => {\n const Render = useAuiState((s) => {\n const Render = s.dataRenderers.renderers[props.name] ?? Fallback;\n if (Array.isArray(Render)) return Render[0] ?? Fallback;\n return Render;\n });\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n data,\n } = {},\n}) => {\n const aui = useAui();\n const part = useAuiState((s) => s.part);\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = aui.part().addToolResult;\n const resume = aui.part().resumeToolCall;\n const respondToApproval = aui.part().respondToToolApproval;\n if (\"Override\" in tools)\n return (\n <tools.Override\n {...part}\n addResult={addResult}\n resume={resume}\n respondToApproval={respondToApproval}\n />\n );\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return (\n <ToolUIDisplay\n {...part}\n Fallback={Tool}\n addResult={addResult}\n resume={resume}\n respondToApproval={respondToApproval}\n />\n );\n }\n\n if (part.status?.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n case \"data\": {\n const Data = data?.by_name?.[part.name] ?? data?.Fallback;\n return <DataUIDisplay {...part} Fallback={Data} />;\n }\n\n default:\n console.warn(`Unknown message part type: ${type}`);\n return null;\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGrouped.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n return (\n <PartByIndexProvider index={partIndex}>\n <MessagePartComponent components={components} />\n </PartByIndexProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.data === next.components?.data &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status = useAuiState(\n (s) => (s.message.status ?? COMPLETE_STATUS) as MessagePartStatus,\n );\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by a custom grouping function.\n *\n * This component allows you to group message parts based on any criteria you define.\n * The grouping function receives all message parts and returns an array of groups,\n * where each group has a key and an array of part indices.\n *\n * @deprecated Prefer `<MessagePrimitive.GroupedParts>` for adjacent\n * grouping — it dispatches all rendering through one `switch (part.type)`\n * and supports nested group paths. Keep this primitive only for\n * non-adjacent clustering (e.g., gathering parts with the same parent-id\n * across the message).\n *\n * @example\n * ```tsx\n * // Group by parent ID (default behavior)\n * <MessagePrimitive.Unstable_PartsGrouped\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ groupKey, indices, children }) => {\n * if (!groupKey) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Parent ID: {groupKey}</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGrouped: FC<\n MessagePrimitiveUnstable_PartsGrouped.Props\n> = ({ groupingFunction, components }) => {\n const contentLength = useAuiState((s) => s.message.parts.length);\n const messageGroups = useMessagePartsGrouped(groupingFunction);\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.groupKey ?? \"ungrouped\"}`}\n groupKey={group.groupKey}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGrouped.displayName =\n \"MessagePrimitive.Unstable_PartsGrouped\";\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.\n *\n * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n Omit<MessagePrimitiveUnstable_PartsGrouped.Props, \"groupingFunction\">\n> = ({ components, ...props }) => {\n return (\n <MessagePrimitiveUnstable_PartsGrouped\n {...props}\n components={components}\n groupingFunction={groupMessagePartsByParentId}\n />\n );\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { Primitive } from "../../utils/Primitive.js";
4
4
  import { useManagedRef } from "../../utils/hooks/useManagedRef.js";
5
5
  import { parseCssLength } from "../thread/topAnchor/topAnchorUtils.js";
6
6
  import { useAui, useAuiState } from "@assistant-ui/store";
7
- import { forwardRef, useCallback } from "react";
7
+ import { forwardRef, useCallback } from "@assistant-ui/tap/react-shim";
8
8
  import { jsx } from "react/jsx-runtime";
9
9
  import { useComposedRefs } from "@radix-ui/react-compose-refs";
10
10
  //#region src/primitives/message/MessageRoot.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"MessageRoot.js","names":[],"sources":["../../../src/primitives/message/MessageRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type ForwardedRef,\n useCallback,\n} from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\nimport { parseCssLength } from \"../thread/topAnchor/topAnchorUtils\";\n\ntype ThreadViewportStore = NonNullable<\n ReturnType<typeof useThreadViewportStore>\n>;\n\nconst useIsHoveringRef = () => {\n const aui = useAui();\n const message = useAuiState(() => aui.message());\n\n const callbackRef = useCallback(\n (el: HTMLElement) => {\n const handleMouseEnter = () => {\n message.setIsHovering(true);\n };\n const handleMouseLeave = () => {\n message.setIsHovering(false);\n };\n\n el.addEventListener(\"mouseenter\", handleMouseEnter);\n el.addEventListener(\"mouseleave\", handleMouseLeave);\n\n if (el.matches(\":hover\")) {\n // TODO this is needed for SSR to work, figure out why\n queueMicrotask(() => message.setIsHovering(true));\n }\n\n return () => {\n el.removeEventListener(\"mouseenter\", handleMouseEnter);\n el.removeEventListener(\"mouseleave\", handleMouseLeave);\n message.setIsHovering(false);\n };\n },\n [message],\n );\n\n return useManagedRef(callbackRef);\n};\n\nconst useIsTopAnchorUser = () => {\n const activeAnchorId = useThreadViewport((s) => s.topAnchorTurn?.anchorId);\n return useAuiState(\n (s) =>\n s.message.role === \"user\" &&\n s.message.index > 0 &&\n s.message.index === s.thread.messages.length - 2 &&\n s.thread.messages.at(-1)?.role === \"assistant\" &&\n (s.message.id === activeAnchorId || s.thread.isRunning),\n );\n};\n\nconst useIsTopAnchorTarget = () => {\n const activeTargetId = useThreadViewport((s) => s.topAnchorTurn?.targetId);\n return useAuiState(\n (s) =>\n s.message.isLast &&\n s.message.role === \"assistant\" &&\n s.message.index >= 1 &&\n s.thread.messages.at(s.message.index - 1)?.role === \"user\" &&\n (s.message.id === activeTargetId || s.thread.isRunning),\n );\n};\n\nconst useTopAnchorUserRef = (\n active: boolean,\n threadViewportStore: ThreadViewportStore,\n) => {\n const callback = useCallback(\n (el: HTMLElement) => {\n if (!active) return;\n return threadViewportStore.getState().registerAnchorElement(el);\n },\n [active, threadViewportStore],\n );\n\n return useManagedRef<HTMLElement>(callback);\n};\n\nconst useTopAnchorTargetRef = ({\n active,\n threadViewportStore,\n}: {\n active: boolean;\n threadViewportStore: ThreadViewportStore;\n}) => {\n const targetRefCallback = useCallback(\n (el: HTMLElement) => {\n if (!active) return;\n const state = threadViewportStore.getState();\n const clamp = state.topAnchorMessageClamp;\n\n return state.registerAnchorTargetElement(el, {\n tallerThan: parseCssLength(clamp.tallerThan, el),\n visibleHeight: parseCssLength(clamp.visibleHeight, el),\n });\n },\n [active, threadViewportStore],\n );\n\n return useManagedRef<HTMLElement>(targetRefCallback);\n};\n\nexport namespace MessagePrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\ntype MessagePrimitiveRootInternalProps = MessagePrimitiveRoot.Props & {\n forwardedRef: ForwardedRef<MessagePrimitiveRoot.Element>;\n};\n\nconst MessagePrimitiveRootDefault = ({\n forwardedRef,\n ...props\n}: MessagePrimitiveRootInternalProps) => {\n const isHoveringRef = useIsHoveringRef();\n const ref = useComposedRefs<HTMLDivElement>(forwardedRef, isHoveringRef);\n const messageId = useAuiState((s) => s.message.id);\n\n return <Primitive.div {...props} ref={ref} data-message-id={messageId} />;\n};\n\nconst MessagePrimitiveRootTopAnchor = ({\n forwardedRef,\n threadViewportStore,\n ...props\n}: MessagePrimitiveRootInternalProps & {\n threadViewportStore: ThreadViewportStore;\n}) => {\n const isHoveringRef = useIsHoveringRef();\n const isTopAnchorUser = useIsTopAnchorUser();\n const isTopAnchorTarget = useIsTopAnchorTarget();\n const topAnchorUserRef = useTopAnchorUserRef(\n isTopAnchorUser,\n threadViewportStore,\n );\n const topAnchorTargetRef = useTopAnchorTargetRef({\n active: isTopAnchorTarget,\n threadViewportStore,\n });\n const ref = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n isHoveringRef,\n topAnchorUserRef,\n topAnchorTargetRef,\n );\n const messageId = useAuiState((s) => s.message.id);\n\n return (\n <Primitive.div\n {...props}\n ref={ref}\n data-message-id={messageId}\n data-aui-top-anchor-user={isTopAnchorUser ? \"\" : undefined}\n data-aui-top-anchor-target={isTopAnchorTarget ? \"\" : undefined}\n />\n );\n};\n\n/**\n * The root container component for a message.\n *\n * This component provides the foundational wrapper for message content and handles\n * hover state management for the message. It automatically tracks when the user\n * is hovering over the message, which can be used by child components like action bars.\n *\n * When `turnAnchor=\"top\"` is set on the viewport, this component automatically\n * registers itself as the top-anchor user message (when it's the previous user\n * message) or as the top-anchor target (when it's the streaming assistant\n * response). No additional component is required.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.Root>\n * <MessagePrimitive.Content />\n * <ActionBarPrimitive.Root>\n * <ActionBarPrimitive.Copy />\n * <ActionBarPrimitive.Edit />\n * </ActionBarPrimitive.Root>\n * </MessagePrimitive.Root>\n * ```\n */\nexport const MessagePrimitiveRoot = forwardRef<\n MessagePrimitiveRoot.Element,\n MessagePrimitiveRoot.Props\n>((props, forwardedRef) => {\n const threadViewportStore = useThreadViewportStore();\n // turnAnchor is initial-only viewport config (see ThreadViewportProvider).\n const turnAnchor = threadViewportStore.getState().turnAnchor;\n\n if (turnAnchor === \"top\") {\n return (\n <MessagePrimitiveRootTopAnchor\n {...props}\n forwardedRef={forwardedRef}\n threadViewportStore={threadViewportStore}\n />\n );\n }\n return <MessagePrimitiveRootDefault {...props} forwardedRef={forwardedRef} />;\n});\n\nMessagePrimitiveRoot.displayName = \"MessagePrimitive.Root\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"MessageRoot.js","names":[],"sources":["../../../src/primitives/message/MessageRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type ForwardedRef,\n useCallback,\n} from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\nimport { parseCssLength } from \"../thread/topAnchor/topAnchorUtils\";\n\ntype ThreadViewportStore = NonNullable<\n ReturnType<typeof useThreadViewportStore>\n>;\n\nconst useIsHoveringRef = () => {\n const aui = useAui();\n const message = useAuiState(() => aui.message());\n\n const callbackRef = useCallback(\n (el: HTMLElement) => {\n const handleMouseEnter = () => {\n message.setIsHovering(true);\n };\n const handleMouseLeave = () => {\n message.setIsHovering(false);\n };\n\n el.addEventListener(\"mouseenter\", handleMouseEnter);\n el.addEventListener(\"mouseleave\", handleMouseLeave);\n\n if (el.matches(\":hover\")) {\n // TODO this is needed for SSR to work, figure out why\n queueMicrotask(() => message.setIsHovering(true));\n }\n\n return () => {\n el.removeEventListener(\"mouseenter\", handleMouseEnter);\n el.removeEventListener(\"mouseleave\", handleMouseLeave);\n message.setIsHovering(false);\n };\n },\n [message],\n );\n\n return useManagedRef(callbackRef);\n};\n\nconst useIsTopAnchorUser = () => {\n const activeAnchorId = useThreadViewport((s) => s.topAnchorTurn?.anchorId);\n return useAuiState(\n (s) =>\n s.message.role === \"user\" &&\n s.message.index > 0 &&\n s.message.index === s.thread.messages.length - 2 &&\n s.thread.messages.at(-1)?.role === \"assistant\" &&\n (s.message.id === activeAnchorId || s.thread.isRunning),\n );\n};\n\nconst useIsTopAnchorTarget = () => {\n const activeTargetId = useThreadViewport((s) => s.topAnchorTurn?.targetId);\n return useAuiState(\n (s) =>\n s.message.isLast &&\n s.message.role === \"assistant\" &&\n s.message.index >= 1 &&\n s.thread.messages.at(s.message.index - 1)?.role === \"user\" &&\n (s.message.id === activeTargetId || s.thread.isRunning),\n );\n};\n\nconst useTopAnchorUserRef = (\n active: boolean,\n threadViewportStore: ThreadViewportStore,\n) => {\n const callback = useCallback(\n (el: HTMLElement) => {\n if (!active) return;\n return threadViewportStore.getState().registerAnchorElement(el);\n },\n [active, threadViewportStore],\n );\n\n return useManagedRef<HTMLElement>(callback);\n};\n\nconst useTopAnchorTargetRef = ({\n active,\n threadViewportStore,\n}: {\n active: boolean;\n threadViewportStore: ThreadViewportStore;\n}) => {\n const targetRefCallback = useCallback(\n (el: HTMLElement) => {\n if (!active) return;\n const state = threadViewportStore.getState();\n const clamp = state.topAnchorMessageClamp;\n\n return state.registerAnchorTargetElement(el, {\n tallerThan: parseCssLength(clamp.tallerThan, el),\n visibleHeight: parseCssLength(clamp.visibleHeight, el),\n });\n },\n [active, threadViewportStore],\n );\n\n return useManagedRef<HTMLElement>(targetRefCallback);\n};\n\nexport namespace MessagePrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\ntype MessagePrimitiveRootInternalProps = MessagePrimitiveRoot.Props & {\n forwardedRef: ForwardedRef<MessagePrimitiveRoot.Element>;\n};\n\nconst MessagePrimitiveRootDefault = ({\n forwardedRef,\n ...props\n}: MessagePrimitiveRootInternalProps) => {\n const isHoveringRef = useIsHoveringRef();\n const ref = useComposedRefs<HTMLDivElement>(forwardedRef, isHoveringRef);\n const messageId = useAuiState((s) => s.message.id);\n\n return <Primitive.div {...props} ref={ref} data-message-id={messageId} />;\n};\n\nconst MessagePrimitiveRootTopAnchor = ({\n forwardedRef,\n threadViewportStore,\n ...props\n}: MessagePrimitiveRootInternalProps & {\n threadViewportStore: ThreadViewportStore;\n}) => {\n const isHoveringRef = useIsHoveringRef();\n const isTopAnchorUser = useIsTopAnchorUser();\n const isTopAnchorTarget = useIsTopAnchorTarget();\n const topAnchorUserRef = useTopAnchorUserRef(\n isTopAnchorUser,\n threadViewportStore,\n );\n const topAnchorTargetRef = useTopAnchorTargetRef({\n active: isTopAnchorTarget,\n threadViewportStore,\n });\n const ref = useComposedRefs<HTMLDivElement>(\n forwardedRef,\n isHoveringRef,\n topAnchorUserRef,\n topAnchorTargetRef,\n );\n const messageId = useAuiState((s) => s.message.id);\n\n return (\n <Primitive.div\n {...props}\n ref={ref}\n data-message-id={messageId}\n data-aui-top-anchor-user={isTopAnchorUser ? \"\" : undefined}\n data-aui-top-anchor-target={isTopAnchorTarget ? \"\" : undefined}\n />\n );\n};\n\n/**\n * The root container component for a message.\n *\n * This component provides the foundational wrapper for message content and handles\n * hover state management for the message. It automatically tracks when the user\n * is hovering over the message, which can be used by child components like action bars.\n *\n * When `turnAnchor=\"top\"` is set on the viewport, this component automatically\n * registers itself as the top-anchor user message (when it's the previous user\n * message) or as the top-anchor target (when it's the streaming assistant\n * response). No additional component is required.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.Root>\n * <MessagePrimitive.Content />\n * <ActionBarPrimitive.Root>\n * <ActionBarPrimitive.Copy />\n * <ActionBarPrimitive.Edit />\n * </ActionBarPrimitive.Root>\n * </MessagePrimitive.Root>\n * ```\n */\nexport const MessagePrimitiveRoot = forwardRef<\n MessagePrimitiveRoot.Element,\n MessagePrimitiveRoot.Props\n>((props, forwardedRef) => {\n const threadViewportStore = useThreadViewportStore();\n // turnAnchor is initial-only viewport config (see ThreadViewportProvider).\n const turnAnchor = threadViewportStore.getState().turnAnchor;\n\n if (turnAnchor === \"top\") {\n return (\n <MessagePrimitiveRootTopAnchor\n {...props}\n forwardedRef={forwardedRef}\n threadViewportStore={threadViewportStore}\n />\n );\n }\n return <MessagePrimitiveRootDefault {...props} forwardedRef={forwardedRef} />;\n});\n\nMessagePrimitiveRoot.displayName = \"MessagePrimitive.Root\";\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
3
  import { useMessagePartImage } from "./useMessagePartImage.js";
4
- import { forwardRef } from "react";
4
+ import { forwardRef } from "@assistant-ui/tap/react-shim";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/primitives/messagePart/MessagePartImage.tsx
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { useMessagePartText } from "./useMessagePartText.js";
3
3
  import { useSmooth } from "../../utils/smooth/useSmooth.js";
4
- import { forwardRef } from "react";
4
+ import { forwardRef } from "@assistant-ui/tap/react-shim";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/primitives/messagePart/MessagePartText.tsx
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { createActionButton } from "../../utils/createActionButton.js";
3
3
  import { useAui } from "@assistant-ui/store";
4
- import { useCallback } from "react";
4
+ import { useCallback } from "@assistant-ui/tap/react-shim";
5
5
  //#region src/primitives/queueItem/QueueItemRemove.ts
6
6
  const useQueueItemRemove = () => {
7
7
  const aui = useAui();
@@ -1 +1 @@
1
- {"version":3,"file":"QueueItemRemove.js","names":[],"sources":["../../../src/primitives/queueItem/QueueItemRemove.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useCallback } from \"react\";\n\nconst useQueueItemRemove = () => {\n const aui = useAui();\n\n const callback = useCallback(() => {\n aui.queueItem().remove();\n }, [aui]);\n\n return callback;\n};\n\nexport namespace QueueItemPrimitiveRemove {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useQueueItemRemove>;\n}\n\n/**\n * A button that removes this item from the queue.\n *\n * @example\n * ```tsx\n * <QueueItemPrimitive.Remove>×</QueueItemPrimitive.Remove>\n * ```\n */\nexport const QueueItemPrimitiveRemove = createActionButton(\n \"QueueItemPrimitive.Remove\",\n useQueueItemRemove,\n);\n"],"mappings":";;;;;AAUA,MAAM,2BAA2B;CAC/B,MAAM,MAAM,OAAO;CAMnB,OAJiB,kBAAkB;EACjC,IAAI,UAAU,EAAE,OAAO;CACzB,GAAG,CAAC,GAAG,CAEO;AAChB;;;;;;;;;AAeA,MAAa,2BAA2B,mBACtC,6BACA,kBACF"}
1
+ {"version":3,"file":"QueueItemRemove.js","names":[],"sources":["../../../src/primitives/queueItem/QueueItemRemove.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useCallback } from \"react\";\n\nconst useQueueItemRemove = () => {\n const aui = useAui();\n\n const callback = useCallback(() => {\n aui.queueItem().remove();\n }, [aui]);\n\n return callback;\n};\n\nexport namespace QueueItemPrimitiveRemove {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useQueueItemRemove>;\n}\n\n/**\n * A button that removes this item from the queue.\n *\n * @example\n * ```tsx\n * <QueueItemPrimitive.Remove>×</QueueItemPrimitive.Remove>\n * ```\n */\nexport const QueueItemPrimitiveRemove = createActionButton(\n \"QueueItemPrimitive.Remove\",\n useQueueItemRemove,\n);\n"],"mappings":";;;;;AAUA,MAAM,2BAA2B;CAC/B,MAAM,MAAM,OAAO;CAMnB,OAJiB,kBAAkB;EACjC,IAAI,UAAU,CAAC,CAAC,OAAO;CACzB,GAAG,CAAC,GAAG,CAEO;AAChB;;;;;;;;;AAeA,MAAa,2BAA2B,mBACtC,6BACA,kBACF"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { createActionButton } from "../../utils/createActionButton.js";
3
3
  import { useAui } from "@assistant-ui/store";
4
- import { useCallback } from "react";
4
+ import { useCallback } from "@assistant-ui/tap/react-shim";
5
5
  //#region src/primitives/queueItem/QueueItemSteer.ts
6
6
  const useQueueItemSteer = () => {
7
7
  const aui = useAui();
@@ -1 +1 @@
1
- {"version":3,"file":"QueueItemSteer.js","names":[],"sources":["../../../src/primitives/queueItem/QueueItemSteer.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useCallback } from \"react\";\n\nconst useQueueItemSteer = () => {\n const aui = useAui();\n\n const callback = useCallback(() => {\n aui.queueItem().steer();\n }, [aui]);\n\n return callback;\n};\n\nexport namespace QueueItemPrimitiveSteer {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useQueueItemSteer>;\n}\n\n/**\n * A button that steers the current run to process this queue item immediately.\n *\n * @example\n * ```tsx\n * <QueueItemPrimitive.Steer>Run Now</QueueItemPrimitive.Steer>\n * ```\n */\nexport const QueueItemPrimitiveSteer = createActionButton(\n \"QueueItemPrimitive.Steer\",\n useQueueItemSteer,\n);\n"],"mappings":";;;;;AAUA,MAAM,0BAA0B;CAC9B,MAAM,MAAM,OAAO;CAMnB,OAJiB,kBAAkB;EACjC,IAAI,UAAU,EAAE,MAAM;CACxB,GAAG,CAAC,GAAG,CAEO;AAChB;;;;;;;;;AAeA,MAAa,0BAA0B,mBACrC,4BACA,iBACF"}
1
+ {"version":3,"file":"QueueItemSteer.js","names":[],"sources":["../../../src/primitives/queueItem/QueueItemSteer.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useCallback } from \"react\";\n\nconst useQueueItemSteer = () => {\n const aui = useAui();\n\n const callback = useCallback(() => {\n aui.queueItem().steer();\n }, [aui]);\n\n return callback;\n};\n\nexport namespace QueueItemPrimitiveSteer {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useQueueItemSteer>;\n}\n\n/**\n * A button that steers the current run to process this queue item immediately.\n *\n * @example\n * ```tsx\n * <QueueItemPrimitive.Steer>Run Now</QueueItemPrimitive.Steer>\n * ```\n */\nexport const QueueItemPrimitiveSteer = createActionButton(\n \"QueueItemPrimitive.Steer\",\n useQueueItemSteer,\n);\n"],"mappings":";;;;;AAUA,MAAM,0BAA0B;CAC9B,MAAM,MAAM,OAAO;CAMnB,OAJiB,kBAAkB;EACjC,IAAI,UAAU,CAAC,CAAC,MAAM;CACxB,GAAG,CAAC,GAAG,CAEO;AAChB;;;;;;;;;AAeA,MAAa,0BAA0B,mBACrC,4BACA,iBACF"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
3
  import { useAuiState } from "@assistant-ui/store";
4
- import { forwardRef } from "react";
4
+ import { forwardRef } from "@assistant-ui/tap/react-shim";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/primitives/queueItem/QueueItemText.tsx
7
7
  /**
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { useCallback, useEffect, useRef } from "react";
2
+ import { useCallback, useEffect, useRef } from "@assistant-ui/tap/react-shim";
3
3
  //#region src/primitives/reasoning/useScrollLock.ts
4
4
  /**
5
5
  * Locks scroll position during collapsible/height animations and hides scrollbar.
@@ -1 +1 @@
1
- {"version":3,"file":"useScrollLock.js","names":[],"sources":["../../../src/primitives/reasoning/useScrollLock.ts"],"sourcesContent":["\"use client\";\n\nimport { type RefObject, useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Locks scroll position during collapsible/height animations and hides scrollbar.\n *\n * This utility prevents page jumps when content height changes during animations,\n * providing a smooth user experience. It finds the nearest scrollable ancestor and\n * temporarily locks its scroll position while the animation completes.\n *\n * - Prevents forced reflows: no layout reads, mutations scoped to scrollable parent only\n * - Reactive: only intercepts scroll events when browser actually adjusts\n * - Cleans up automatically after animation duration\n *\n * @param animatedElementRef - Ref to the animated element\n * @param animationDuration - Lock duration in milliseconds\n * @returns Function to activate the scroll lock\n *\n * @example\n * ```tsx\n * const collapsibleRef = useRef<HTMLDivElement>(null);\n * const lockScroll = useScrollLock(collapsibleRef, 200);\n *\n * const handleCollapse = () => {\n * lockScroll(); // Lock scroll before collapsing\n * setIsOpen(false);\n * };\n * ```\n */\nexport const useScrollLock = <T extends HTMLElement = HTMLElement>(\n animatedElementRef: RefObject<T | null>,\n animationDuration: number,\n) => {\n const scrollContainerRef = useRef<HTMLElement | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\n }, []);\n\n const lockScroll = useCallback(() => {\n cleanupRef.current?.();\n\n (function findScrollableAncestor() {\n if (scrollContainerRef.current || !animatedElementRef.current) return;\n\n let el: HTMLElement | null = animatedElementRef.current;\n while (el) {\n const { overflowY } = getComputedStyle(el);\n if (overflowY === \"scroll\" || overflowY === \"auto\") {\n scrollContainerRef.current = el;\n break;\n }\n el = el.parentElement;\n }\n })();\n\n const scrollContainer = scrollContainerRef.current;\n if (!scrollContainer) return;\n\n const scrollPosition = scrollContainer.scrollTop;\n const scrollbarWidth = scrollContainer.style.scrollbarWidth;\n\n scrollContainer.style.scrollbarWidth = \"none\";\n\n const resetPosition = () => (scrollContainer.scrollTop = scrollPosition);\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n cleanupRef.current = null;\n }, animationDuration);\n\n cleanupRef.current = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n };\n }, [animationDuration, animatedElementRef]);\n\n return lockScroll;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,iBACX,oBACA,sBACG;CACH,MAAM,qBAAqB,OAA2B,IAAI;CAC1D,MAAM,aAAa,OAA4B,IAAI;CAEnD,gBAAgB;EACd,aAAa;GACX,WAAW,UAAU;EACvB;CACF,GAAG,CAAC,CAAC;CA2CL,OAzCmB,kBAAkB;EACnC,WAAW,UAAU;EAErB,CAAC,SAAS,yBAAyB;GACjC,IAAI,mBAAmB,WAAW,CAAC,mBAAmB,SAAS;GAE/D,IAAI,KAAyB,mBAAmB;GAChD,OAAO,IAAI;IACT,MAAM,EAAE,cAAc,iBAAiB,EAAE;IACzC,IAAI,cAAc,YAAY,cAAc,QAAQ;KAClD,mBAAmB,UAAU;KAC7B;IACF;IACA,KAAK,GAAG;GACV;EACF,GAAG;EAEH,MAAM,kBAAkB,mBAAmB;EAC3C,IAAI,CAAC,iBAAiB;EAEtB,MAAM,iBAAiB,gBAAgB;EACvC,MAAM,iBAAiB,gBAAgB,MAAM;EAE7C,gBAAgB,MAAM,iBAAiB;EAEvC,MAAM,sBAAuB,gBAAgB,YAAY;EACzD,gBAAgB,iBAAiB,UAAU,aAAa;EAExD,MAAM,YAAY,iBAAiB;GACjC,gBAAgB,oBAAoB,UAAU,aAAa;GAC3D,gBAAgB,MAAM,iBAAiB;GACvC,WAAW,UAAU;EACvB,GAAG,iBAAiB;EAEpB,WAAW,gBAAgB;GACzB,aAAa,SAAS;GACtB,gBAAgB,oBAAoB,UAAU,aAAa;GAC3D,gBAAgB,MAAM,iBAAiB;EACzC;CACF,GAAG,CAAC,mBAAmB,kBAAkB,CAEzB;AAClB"}
1
+ {"version":3,"file":"useScrollLock.js","names":[],"sources":["../../../src/primitives/reasoning/useScrollLock.ts"],"sourcesContent":["\"use client\";\n\nimport { type RefObject, useCallback, useEffect, useRef } from \"react\";\n\n/**\n * Locks scroll position during collapsible/height animations and hides scrollbar.\n *\n * This utility prevents page jumps when content height changes during animations,\n * providing a smooth user experience. It finds the nearest scrollable ancestor and\n * temporarily locks its scroll position while the animation completes.\n *\n * - Prevents forced reflows: no layout reads, mutations scoped to scrollable parent only\n * - Reactive: only intercepts scroll events when browser actually adjusts\n * - Cleans up automatically after animation duration\n *\n * @param animatedElementRef - Ref to the animated element\n * @param animationDuration - Lock duration in milliseconds\n * @returns Function to activate the scroll lock\n *\n * @example\n * ```tsx\n * const collapsibleRef = useRef<HTMLDivElement>(null);\n * const lockScroll = useScrollLock(collapsibleRef, 200);\n *\n * const handleCollapse = () => {\n * lockScroll(); // Lock scroll before collapsing\n * setIsOpen(false);\n * };\n * ```\n */\nexport const useScrollLock = <T extends HTMLElement = HTMLElement>(\n animatedElementRef: RefObject<T | null>,\n animationDuration: number,\n) => {\n const scrollContainerRef = useRef<HTMLElement | null>(null);\n const cleanupRef = useRef<(() => void) | null>(null);\n\n useEffect(() => {\n return () => {\n cleanupRef.current?.();\n };\n }, []);\n\n const lockScroll = useCallback(() => {\n cleanupRef.current?.();\n\n (function findScrollableAncestor() {\n if (scrollContainerRef.current || !animatedElementRef.current) return;\n\n let el: HTMLElement | null = animatedElementRef.current;\n while (el) {\n const { overflowY } = getComputedStyle(el);\n if (overflowY === \"scroll\" || overflowY === \"auto\") {\n scrollContainerRef.current = el;\n break;\n }\n el = el.parentElement;\n }\n })();\n\n const scrollContainer = scrollContainerRef.current;\n if (!scrollContainer) return;\n\n const scrollPosition = scrollContainer.scrollTop;\n const scrollbarWidth = scrollContainer.style.scrollbarWidth;\n\n scrollContainer.style.scrollbarWidth = \"none\";\n\n const resetPosition = () => (scrollContainer.scrollTop = scrollPosition);\n scrollContainer.addEventListener(\"scroll\", resetPosition);\n\n const timeoutId = setTimeout(() => {\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n cleanupRef.current = null;\n }, animationDuration);\n\n cleanupRef.current = () => {\n clearTimeout(timeoutId);\n scrollContainer.removeEventListener(\"scroll\", resetPosition);\n scrollContainer.style.scrollbarWidth = scrollbarWidth;\n };\n }, [animationDuration, animatedElementRef]);\n\n return lockScroll;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,iBACX,oBACA,sBACG;CACH,MAAM,qBAAqB,OAA2B,IAAI;CAC1D,MAAM,aAAa,OAA4B,IAAI;CAEnD,gBAAgB;EACd,aAAa;GACX,WAAW,UAAU;EACvB;CACF,GAAG,CAAC,CAAC;CA2CL,OAzCmB,kBAAkB;EACnC,WAAW,UAAU;EAErB,CAAC,SAAS,yBAAyB;GACjC,IAAI,mBAAmB,WAAW,CAAC,mBAAmB,SAAS;GAE/D,IAAI,KAAyB,mBAAmB;GAChD,OAAO,IAAI;IACT,MAAM,EAAE,cAAc,iBAAiB,EAAE;IACzC,IAAI,cAAc,YAAY,cAAc,QAAQ;KAClD,mBAAmB,UAAU;KAC7B;IACF;IACA,KAAK,GAAG;GACV;EACF,EAAA,CAAG;EAEH,MAAM,kBAAkB,mBAAmB;EAC3C,IAAI,CAAC,iBAAiB;EAEtB,MAAM,iBAAiB,gBAAgB;EACvC,MAAM,iBAAiB,gBAAgB,MAAM;EAE7C,gBAAgB,MAAM,iBAAiB;EAEvC,MAAM,sBAAuB,gBAAgB,YAAY;EACzD,gBAAgB,iBAAiB,UAAU,aAAa;EAExD,MAAM,YAAY,iBAAiB;GACjC,gBAAgB,oBAAoB,UAAU,aAAa;GAC3D,gBAAgB,MAAM,iBAAiB;GACvC,WAAW,UAAU;EACvB,GAAG,iBAAiB;EAEpB,WAAW,gBAAgB;GACzB,aAAa,SAAS;GACtB,gBAAgB,oBAAoB,UAAU,aAAa;GAC3D,gBAAgB,MAAM,iBAAiB;EACzC;CACF,GAAG,CAAC,mBAAmB,kBAAkB,CAEzB;AAClB"}
@@ -2,7 +2,7 @@
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
3
  import { useSelectionToolbarInfo } from "./SelectionToolbarRoot.js";
4
4
  import { useAui } from "@assistant-ui/store";
5
- import { forwardRef, useCallback } from "react";
5
+ import { forwardRef, useCallback } from "@assistant-ui/tap/react-shim";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { composeEventHandlers } from "@radix-ui/primitive";
8
8
  //#region src/primitives/selectionToolbar/SelectionToolbarQuote.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"SelectionToolbarQuote.js","names":[],"sources":["../../../src/primitives/selectionToolbar/SelectionToolbarQuote.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n forwardRef,\n useCallback,\n} from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useSelectionToolbarInfo } from \"./SelectionToolbarRoot\";\n\nexport namespace SelectionToolbarPrimitiveQuote {\n export type Element = ComponentRef<typeof Primitive.button>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;\n}\n\n/**\n * A button that quotes the currently selected text.\n *\n * Must be placed inside `SelectionToolbarPrimitive.Root`. Reads the\n * selection info from context (captured by the Root), sets it as a\n * quote in the thread composer, and clears the selection.\n *\n * @example\n * ```tsx\n * <SelectionToolbarPrimitive.Quote>\n * <QuoteIcon /> Quote\n * </SelectionToolbarPrimitive.Quote>\n * ```\n */\nexport const SelectionToolbarPrimitiveQuote = forwardRef<\n SelectionToolbarPrimitiveQuote.Element,\n SelectionToolbarPrimitiveQuote.Props\n>(({ onClick, disabled, ...props }, forwardedRef) => {\n const aui = useAui();\n const info = useSelectionToolbarInfo();\n\n const handleClick = useCallback(() => {\n if (!info) return;\n\n aui.thread().composer().setQuote({\n text: info.text,\n messageId: info.messageId,\n });\n\n window.getSelection()?.removeAllRanges();\n }, [aui, info]);\n\n return (\n <Primitive.button\n type=\"button\"\n {...props}\n ref={forwardedRef}\n disabled={disabled || !info}\n onClick={composeEventHandlers(onClick, handleClick)}\n />\n );\n});\n\nSelectionToolbarPrimitiveQuote.displayName = \"SelectionToolbarPrimitive.Quote\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,iCAAiC,YAG3C,EAAE,SAAS,UAAU,GAAG,SAAS,iBAAiB;CACnD,MAAM,MAAM,OAAO;CACnB,MAAM,OAAO,wBAAwB;CAErC,MAAM,cAAc,kBAAkB;EACpC,IAAI,CAAC,MAAM;EAEX,IAAI,OAAO,EAAE,SAAS,EAAE,SAAS;GAC/B,MAAM,KAAK;GACX,WAAW,KAAK;EAClB,CAAC;EAED,OAAO,aAAa,GAAG,gBAAgB;CACzC,GAAG,CAAC,KAAK,IAAI,CAAC;CAEd,OACE,oBAAC,UAAU,QAAX;EACE,MAAK;EACL,GAAI;EACJ,KAAK;EACL,UAAU,YAAY,CAAC;EACvB,SAAS,qBAAqB,SAAS,WAAW;CACnD,CAAA;AAEL,CAAC;AAED,+BAA+B,cAAc"}
1
+ {"version":3,"file":"SelectionToolbarQuote.js","names":[],"sources":["../../../src/primitives/selectionToolbar/SelectionToolbarQuote.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport { composeEventHandlers } from \"@radix-ui/primitive\";\nimport {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n forwardRef,\n useCallback,\n} from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useSelectionToolbarInfo } from \"./SelectionToolbarRoot\";\n\nexport namespace SelectionToolbarPrimitiveQuote {\n export type Element = ComponentRef<typeof Primitive.button>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.button>;\n}\n\n/**\n * A button that quotes the currently selected text.\n *\n * Must be placed inside `SelectionToolbarPrimitive.Root`. Reads the\n * selection info from context (captured by the Root), sets it as a\n * quote in the thread composer, and clears the selection.\n *\n * @example\n * ```tsx\n * <SelectionToolbarPrimitive.Quote>\n * <QuoteIcon /> Quote\n * </SelectionToolbarPrimitive.Quote>\n * ```\n */\nexport const SelectionToolbarPrimitiveQuote = forwardRef<\n SelectionToolbarPrimitiveQuote.Element,\n SelectionToolbarPrimitiveQuote.Props\n>(({ onClick, disabled, ...props }, forwardedRef) => {\n const aui = useAui();\n const info = useSelectionToolbarInfo();\n\n const handleClick = useCallback(() => {\n if (!info) return;\n\n aui.thread().composer().setQuote({\n text: info.text,\n messageId: info.messageId,\n });\n\n window.getSelection()?.removeAllRanges();\n }, [aui, info]);\n\n return (\n <Primitive.button\n type=\"button\"\n {...props}\n ref={forwardedRef}\n disabled={disabled || !info}\n onClick={composeEventHandlers(onClick, handleClick)}\n />\n );\n});\n\nSelectionToolbarPrimitiveQuote.displayName = \"SelectionToolbarPrimitive.Quote\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,iCAAiC,YAG3C,EAAE,SAAS,UAAU,GAAG,SAAS,iBAAiB;CACnD,MAAM,MAAM,OAAO;CACnB,MAAM,OAAO,wBAAwB;CAErC,MAAM,cAAc,kBAAkB;EACpC,IAAI,CAAC,MAAM;EAEX,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS;GAC/B,MAAM,KAAK;GACX,WAAW,KAAK;EAClB,CAAC;EAED,OAAO,aAAa,CAAC,EAAE,gBAAgB;CACzC,GAAG,CAAC,KAAK,IAAI,CAAC;CAEd,OACE,oBAAC,UAAU,QAAX;EACE,MAAK;EACL,GAAI;EACJ,KAAK;EACL,UAAU,YAAY,CAAC;EACvB,SAAS,qBAAqB,SAAS,WAAW;CACnD,CAAA;AAEL,CAAC;AAED,+BAA+B,cAAc"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
3
  import { getSelectionMessageId } from "../../utils/getSelectionMessageId.js";
4
- import { createContext, forwardRef, useContext, useEffect, useState } from "react";
4
+ import { createContext, forwardRef, useContext, useEffect, useState } from "@assistant-ui/tap/react-shim";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { createPortal } from "react-dom";
7
7
  //#region src/primitives/selectionToolbar/SelectionToolbarRoot.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"SelectionToolbarRoot.js","names":[],"sources":["../../../src/primitives/selectionToolbar/SelectionToolbarRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n createContext,\n forwardRef,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { getSelectionMessageId } from \"../../utils/getSelectionMessageId\";\n\ntype SelectionInfo = {\n text: string;\n messageId: string;\n rect: DOMRect;\n};\n\nconst SelectionToolbarContext = createContext<SelectionInfo | null>(null);\n\nexport const useSelectionToolbarInfo = () =>\n useContext(SelectionToolbarContext);\n\nexport namespace SelectionToolbarPrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\n/**\n * A floating toolbar that appears when text is selected within a message.\n *\n * Listens for mouse and keyboard selection events, validates that the\n * selection is within a single message, and renders a positioned portal\n * near the selection. Prevents mousedown from clearing the selection.\n *\n * @example\n * ```tsx\n * <SelectionToolbarPrimitive.Root>\n * <SelectionToolbarPrimitive.Quote>Quote</SelectionToolbarPrimitive.Quote>\n * </SelectionToolbarPrimitive.Root>\n * ```\n */\nexport const SelectionToolbarPrimitiveRoot = forwardRef<\n SelectionToolbarPrimitiveRoot.Element,\n SelectionToolbarPrimitiveRoot.Props\n>(({ onMouseDown, style, ...props }, forwardedRef) => {\n const [info, setInfo] = useState<SelectionInfo | null>(null);\n\n useEffect(() => {\n const checkSelection = () => {\n requestAnimationFrame(() => {\n const sel = window.getSelection();\n if (!sel || sel.isCollapsed) {\n setInfo(null);\n return;\n }\n\n const text = sel.toString().trim();\n if (!text) {\n setInfo(null);\n return;\n }\n\n const messageId = getSelectionMessageId(sel);\n if (!messageId) {\n setInfo(null);\n return;\n }\n\n const range = sel.getRangeAt(0);\n const rect = range.getBoundingClientRect();\n setInfo({ text, messageId, rect });\n });\n };\n\n const handleSelectionCollapse = () => {\n const sel = window.getSelection();\n if (!sel || sel.isCollapsed) {\n setInfo(null);\n }\n };\n\n const handleScroll = () => {\n setInfo(null);\n };\n\n document.addEventListener(\"mouseup\", checkSelection);\n document.addEventListener(\"keyup\", checkSelection);\n document.addEventListener(\"selectionchange\", handleSelectionCollapse);\n document.addEventListener(\"scroll\", handleScroll, true);\n\n return () => {\n document.removeEventListener(\"mouseup\", checkSelection);\n document.removeEventListener(\"keyup\", checkSelection);\n document.removeEventListener(\"selectionchange\", handleSelectionCollapse);\n document.removeEventListener(\"scroll\", handleScroll, true);\n };\n }, []);\n\n if (!info) return null;\n\n const positionStyle: React.CSSProperties = {\n position: \"fixed\",\n top: `${info.rect.top - 8}px`,\n left: `${info.rect.left + info.rect.width / 2}px`,\n transform: \"translate(-50%, -100%)\",\n zIndex: 50,\n ...style,\n };\n\n return createPortal(\n <SelectionToolbarContext.Provider value={info}>\n <Primitive.div\n {...props}\n ref={forwardedRef}\n style={positionStyle}\n onMouseDown={(e) => {\n // Prevent mousedown from clearing the text selection\n e.preventDefault();\n onMouseDown?.(e);\n }}\n />\n </SelectionToolbarContext.Provider>,\n document.body,\n );\n});\n\nSelectionToolbarPrimitiveRoot.displayName = \"SelectionToolbarPrimitive.Root\";\n"],"mappings":";;;;;;;AAqBA,MAAM,0BAA0B,cAAoC,IAAI;AAExE,MAAa,gCACX,WAAW,uBAAuB;;;;;;;;;;;;;;;AAqBpC,MAAa,gCAAgC,YAG1C,EAAE,aAAa,OAAO,GAAG,SAAS,iBAAiB;CACpD,MAAM,CAAC,MAAM,WAAW,SAA+B,IAAI;CAE3D,gBAAgB;EACd,MAAM,uBAAuB;GAC3B,4BAA4B;IAC1B,MAAM,MAAM,OAAO,aAAa;IAChC,IAAI,CAAC,OAAO,IAAI,aAAa;KAC3B,QAAQ,IAAI;KACZ;IACF;IAEA,MAAM,OAAO,IAAI,SAAS,EAAE,KAAK;IACjC,IAAI,CAAC,MAAM;KACT,QAAQ,IAAI;KACZ;IACF;IAEA,MAAM,YAAY,sBAAsB,GAAG;IAC3C,IAAI,CAAC,WAAW;KACd,QAAQ,IAAI;KACZ;IACF;IAIA,QAAQ;KAAE;KAAM;KAAW,MAFb,IAAI,WAAW,CACZ,EAAE,sBACW;IAAE,CAAC;GACnC,CAAC;EACH;EAEA,MAAM,gCAAgC;GACpC,MAAM,MAAM,OAAO,aAAa;GAChC,IAAI,CAAC,OAAO,IAAI,aACd,QAAQ,IAAI;EAEhB;EAEA,MAAM,qBAAqB;GACzB,QAAQ,IAAI;EACd;EAEA,SAAS,iBAAiB,WAAW,cAAc;EACnD,SAAS,iBAAiB,SAAS,cAAc;EACjD,SAAS,iBAAiB,mBAAmB,uBAAuB;EACpE,SAAS,iBAAiB,UAAU,cAAc,IAAI;EAEtD,aAAa;GACX,SAAS,oBAAoB,WAAW,cAAc;GACtD,SAAS,oBAAoB,SAAS,cAAc;GACpD,SAAS,oBAAoB,mBAAmB,uBAAuB;GACvE,SAAS,oBAAoB,UAAU,cAAc,IAAI;EAC3D;CACF,GAAG,CAAC,CAAC;CAEL,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,gBAAqC;EACzC,UAAU;EACV,KAAK,GAAG,KAAK,KAAK,MAAM,EAAE;EAC1B,MAAM,GAAG,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,EAAE;EAC9C,WAAW;EACX,QAAQ;EACR,GAAG;CACL;CAEA,OAAO,aACL,oBAAC,wBAAwB,UAAzB;EAAkC,OAAO;YACvC,oBAAC,UAAU,KAAX;GACE,GAAI;GACJ,KAAK;GACL,OAAO;GACP,cAAc,MAAM;IAElB,EAAE,eAAe;IACjB,cAAc,CAAC;GACjB;EACD,CAAA;CAC+B,CAAA,GAClC,SAAS,IACX;AACF,CAAC;AAED,8BAA8B,cAAc"}
1
+ {"version":3,"file":"SelectionToolbarRoot.js","names":[],"sources":["../../../src/primitives/selectionToolbar/SelectionToolbarRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentPropsWithoutRef,\n type ComponentRef,\n createContext,\n forwardRef,\n useContext,\n useEffect,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { getSelectionMessageId } from \"../../utils/getSelectionMessageId\";\n\ntype SelectionInfo = {\n text: string;\n messageId: string;\n rect: DOMRect;\n};\n\nconst SelectionToolbarContext = createContext<SelectionInfo | null>(null);\n\nexport const useSelectionToolbarInfo = () =>\n useContext(SelectionToolbarContext);\n\nexport namespace SelectionToolbarPrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\n/**\n * A floating toolbar that appears when text is selected within a message.\n *\n * Listens for mouse and keyboard selection events, validates that the\n * selection is within a single message, and renders a positioned portal\n * near the selection. Prevents mousedown from clearing the selection.\n *\n * @example\n * ```tsx\n * <SelectionToolbarPrimitive.Root>\n * <SelectionToolbarPrimitive.Quote>Quote</SelectionToolbarPrimitive.Quote>\n * </SelectionToolbarPrimitive.Root>\n * ```\n */\nexport const SelectionToolbarPrimitiveRoot = forwardRef<\n SelectionToolbarPrimitiveRoot.Element,\n SelectionToolbarPrimitiveRoot.Props\n>(({ onMouseDown, style, ...props }, forwardedRef) => {\n const [info, setInfo] = useState<SelectionInfo | null>(null);\n\n useEffect(() => {\n const checkSelection = () => {\n requestAnimationFrame(() => {\n const sel = window.getSelection();\n if (!sel || sel.isCollapsed) {\n setInfo(null);\n return;\n }\n\n const text = sel.toString().trim();\n if (!text) {\n setInfo(null);\n return;\n }\n\n const messageId = getSelectionMessageId(sel);\n if (!messageId) {\n setInfo(null);\n return;\n }\n\n const range = sel.getRangeAt(0);\n const rect = range.getBoundingClientRect();\n setInfo({ text, messageId, rect });\n });\n };\n\n const handleSelectionCollapse = () => {\n const sel = window.getSelection();\n if (!sel || sel.isCollapsed) {\n setInfo(null);\n }\n };\n\n const handleScroll = () => {\n setInfo(null);\n };\n\n document.addEventListener(\"mouseup\", checkSelection);\n document.addEventListener(\"keyup\", checkSelection);\n document.addEventListener(\"selectionchange\", handleSelectionCollapse);\n document.addEventListener(\"scroll\", handleScroll, true);\n\n return () => {\n document.removeEventListener(\"mouseup\", checkSelection);\n document.removeEventListener(\"keyup\", checkSelection);\n document.removeEventListener(\"selectionchange\", handleSelectionCollapse);\n document.removeEventListener(\"scroll\", handleScroll, true);\n };\n }, []);\n\n if (!info) return null;\n\n const positionStyle: React.CSSProperties = {\n position: \"fixed\",\n top: `${info.rect.top - 8}px`,\n left: `${info.rect.left + info.rect.width / 2}px`,\n transform: \"translate(-50%, -100%)\",\n zIndex: 50,\n ...style,\n };\n\n return createPortal(\n <SelectionToolbarContext.Provider value={info}>\n <Primitive.div\n {...props}\n ref={forwardedRef}\n style={positionStyle}\n onMouseDown={(e) => {\n // Prevent mousedown from clearing the text selection\n e.preventDefault();\n onMouseDown?.(e);\n }}\n />\n </SelectionToolbarContext.Provider>,\n document.body,\n );\n});\n\nSelectionToolbarPrimitiveRoot.displayName = \"SelectionToolbarPrimitive.Root\";\n"],"mappings":";;;;;;;AAqBA,MAAM,0BAA0B,cAAoC,IAAI;AAExE,MAAa,gCACX,WAAW,uBAAuB;;;;;;;;;;;;;;;AAqBpC,MAAa,gCAAgC,YAG1C,EAAE,aAAa,OAAO,GAAG,SAAS,iBAAiB;CACpD,MAAM,CAAC,MAAM,WAAW,SAA+B,IAAI;CAE3D,gBAAgB;EACd,MAAM,uBAAuB;GAC3B,4BAA4B;IAC1B,MAAM,MAAM,OAAO,aAAa;IAChC,IAAI,CAAC,OAAO,IAAI,aAAa;KAC3B,QAAQ,IAAI;KACZ;IACF;IAEA,MAAM,OAAO,IAAI,SAAS,CAAC,CAAC,KAAK;IACjC,IAAI,CAAC,MAAM;KACT,QAAQ,IAAI;KACZ;IACF;IAEA,MAAM,YAAY,sBAAsB,GAAG;IAC3C,IAAI,CAAC,WAAW;KACd,QAAQ,IAAI;KACZ;IACF;IAIA,QAAQ;KAAE;KAAM;KAAW,MAFb,IAAI,WAAW,CACZ,CAAC,CAAC,sBACW;IAAE,CAAC;GACnC,CAAC;EACH;EAEA,MAAM,gCAAgC;GACpC,MAAM,MAAM,OAAO,aAAa;GAChC,IAAI,CAAC,OAAO,IAAI,aACd,QAAQ,IAAI;EAEhB;EAEA,MAAM,qBAAqB;GACzB,QAAQ,IAAI;EACd;EAEA,SAAS,iBAAiB,WAAW,cAAc;EACnD,SAAS,iBAAiB,SAAS,cAAc;EACjD,SAAS,iBAAiB,mBAAmB,uBAAuB;EACpE,SAAS,iBAAiB,UAAU,cAAc,IAAI;EAEtD,aAAa;GACX,SAAS,oBAAoB,WAAW,cAAc;GACtD,SAAS,oBAAoB,SAAS,cAAc;GACpD,SAAS,oBAAoB,mBAAmB,uBAAuB;GACvE,SAAS,oBAAoB,UAAU,cAAc,IAAI;EAC3D;CACF,GAAG,CAAC,CAAC;CAEL,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,gBAAqC;EACzC,UAAU;EACV,KAAK,GAAG,KAAK,KAAK,MAAM,EAAE;EAC1B,MAAM,GAAG,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,EAAE;EAC9C,WAAW;EACX,QAAQ;EACR,GAAG;CACL;CAEA,OAAO,aACL,oBAAC,wBAAwB,UAAzB;EAAkC,OAAO;YACvC,oBAAC,UAAU,KAAX;GACE,GAAI;GACJ,KAAK;GACL,OAAO;GACP,cAAc,MAAM;IAElB,EAAE,eAAe;IACjB,cAAc,CAAC;GACjB;EACD,CAAA;CAC+B,CAAA,GAClC,SAAS,IACX;AACF,CAAC;AAED,8BAA8B,cAAc"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
3
  import { useAuiState } from "@assistant-ui/store";
4
- import { forwardRef } from "react";
4
+ import { forwardRef } from "@assistant-ui/tap/react-shim";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/primitives/suggestion/SuggestionDescription.tsx
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
3
  import { useAuiState } from "@assistant-ui/store";
4
- import { forwardRef } from "react";
4
+ import { forwardRef } from "@assistant-ui/tap/react-shim";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/primitives/suggestion/SuggestionTitle.tsx
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { createActionButton } from "../../utils/createActionButton.js";
3
3
  import { useAui, useAuiState } from "@assistant-ui/store";
4
- import { useCallback } from "react";
4
+ import { useCallback } from "@assistant-ui/tap/react-shim";
5
5
  //#region src/primitives/suggestion/SuggestionTrigger.ts
6
6
  const useSuggestionTrigger = ({ send, clearComposer = true }) => {
7
7
  const aui = useAui();
@@ -1 +1 @@
1
- {"version":3,"file":"SuggestionTrigger.js","names":[],"sources":["../../../src/primitives/suggestion/SuggestionTrigger.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useCallback } from \"react\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\n\nconst useSuggestionTrigger = ({\n send,\n clearComposer = true,\n}: {\n /**\n * When true, automatically sends the message.\n * When false, replaces or appends the composer text with the suggestion - depending on the value of `clearComposer`.\n */\n send?: boolean | undefined;\n\n /**\n * Whether to clear the composer after sending.\n * When send is set to false, determines if composer text is replaced with suggestion (true, default),\n * or if it's appended to the composer text (false).\n *\n * @default true\n */\n clearComposer?: boolean | undefined;\n}) => {\n const aui = useAui();\n const disabled = useAuiState((s) => s.thread.isDisabled);\n const prompt = useAuiState((s) => s.suggestion.prompt);\n\n const resolvedSend = send ?? false;\n\n const callback = useCallback(() => {\n const isRunning = aui.thread().getState().isRunning;\n\n if (resolvedSend && !isRunning) {\n aui.thread().append({\n content: [{ type: \"text\", text: prompt }],\n runConfig: aui.composer().getState().runConfig,\n });\n if (clearComposer) {\n aui.composer().setText(\"\");\n }\n } else {\n if (clearComposer) {\n aui.composer().setText(prompt);\n } else {\n const currentText = aui.composer().getState().text;\n aui\n .composer()\n .setText(currentText.trim() ? `${currentText} ${prompt}` : prompt);\n }\n }\n }, [aui, resolvedSend, clearComposer, prompt]);\n\n if (disabled) return null;\n return callback;\n};\n\nexport namespace SuggestionPrimitiveTrigger {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useSuggestionTrigger>;\n}\n\n/**\n * A button that triggers the suggestion action (send or insert into composer).\n *\n * @example\n * ```tsx\n * <SuggestionPrimitive.Trigger send>\n * Click me\n * </SuggestionPrimitive.Trigger>\n * ```\n */\nexport const SuggestionPrimitiveTrigger = createActionButton(\n \"SuggestionPrimitive.Trigger\",\n useSuggestionTrigger,\n [\"send\", \"clearComposer\"],\n);\n"],"mappings":";;;;;AAUA,MAAM,wBAAwB,EAC5B,MACA,gBAAgB,WAgBZ;CACJ,MAAM,MAAM,OAAO;CACnB,MAAM,WAAW,aAAa,MAAM,EAAE,OAAO,UAAU;CACvD,MAAM,SAAS,aAAa,MAAM,EAAE,WAAW,MAAM;CAErD,MAAM,eAAe,QAAQ;CAE7B,MAAM,WAAW,kBAAkB;EACjC,MAAM,YAAY,IAAI,OAAO,EAAE,SAAS,EAAE;EAE1C,IAAI,gBAAgB,CAAC,WAAW;GAC9B,IAAI,OAAO,EAAE,OAAO;IAClB,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;IAAO,CAAC;IACxC,WAAW,IAAI,SAAS,EAAE,SAAS,EAAE;GACvC,CAAC;GACD,IAAI,eACF,IAAI,SAAS,EAAE,QAAQ,EAAE;EAE7B,OACE,IAAI,eACF,IAAI,SAAS,EAAE,QAAQ,MAAM;OACxB;GACL,MAAM,cAAc,IAAI,SAAS,EAAE,SAAS,EAAE;GAC9C,IACG,SAAS,EACT,QAAQ,YAAY,KAAK,IAAI,GAAG,YAAY,GAAG,WAAW,MAAM;EACrE;CAEJ,GAAG;EAAC;EAAK;EAAc;EAAe;CAAM,CAAC;CAE7C,IAAI,UAAU,OAAO;CACrB,OAAO;AACT;;;;;;;;;;;AAiBA,MAAa,6BAA6B,mBACxC,+BACA,sBACA,CAAC,QAAQ,eAAe,CAC1B"}
1
+ {"version":3,"file":"SuggestionTrigger.js","names":[],"sources":["../../../src/primitives/suggestion/SuggestionTrigger.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useCallback } from \"react\";\nimport { useAuiState, useAui } from \"@assistant-ui/store\";\n\nconst useSuggestionTrigger = ({\n send,\n clearComposer = true,\n}: {\n /**\n * When true, automatically sends the message.\n * When false, replaces or appends the composer text with the suggestion - depending on the value of `clearComposer`.\n */\n send?: boolean | undefined;\n\n /**\n * Whether to clear the composer after sending.\n * When send is set to false, determines if composer text is replaced with suggestion (true, default),\n * or if it's appended to the composer text (false).\n *\n * @default true\n */\n clearComposer?: boolean | undefined;\n}) => {\n const aui = useAui();\n const disabled = useAuiState((s) => s.thread.isDisabled);\n const prompt = useAuiState((s) => s.suggestion.prompt);\n\n const resolvedSend = send ?? false;\n\n const callback = useCallback(() => {\n const isRunning = aui.thread().getState().isRunning;\n\n if (resolvedSend && !isRunning) {\n aui.thread().append({\n content: [{ type: \"text\", text: prompt }],\n runConfig: aui.composer().getState().runConfig,\n });\n if (clearComposer) {\n aui.composer().setText(\"\");\n }\n } else {\n if (clearComposer) {\n aui.composer().setText(prompt);\n } else {\n const currentText = aui.composer().getState().text;\n aui\n .composer()\n .setText(currentText.trim() ? `${currentText} ${prompt}` : prompt);\n }\n }\n }, [aui, resolvedSend, clearComposer, prompt]);\n\n if (disabled) return null;\n return callback;\n};\n\nexport namespace SuggestionPrimitiveTrigger {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useSuggestionTrigger>;\n}\n\n/**\n * A button that triggers the suggestion action (send or insert into composer).\n *\n * @example\n * ```tsx\n * <SuggestionPrimitive.Trigger send>\n * Click me\n * </SuggestionPrimitive.Trigger>\n * ```\n */\nexport const SuggestionPrimitiveTrigger = createActionButton(\n \"SuggestionPrimitive.Trigger\",\n useSuggestionTrigger,\n [\"send\", \"clearComposer\"],\n);\n"],"mappings":";;;;;AAUA,MAAM,wBAAwB,EAC5B,MACA,gBAAgB,WAgBZ;CACJ,MAAM,MAAM,OAAO;CACnB,MAAM,WAAW,aAAa,MAAM,EAAE,OAAO,UAAU;CACvD,MAAM,SAAS,aAAa,MAAM,EAAE,WAAW,MAAM;CAErD,MAAM,eAAe,QAAQ;CAE7B,MAAM,WAAW,kBAAkB;EACjC,MAAM,YAAY,IAAI,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;EAE1C,IAAI,gBAAgB,CAAC,WAAW;GAC9B,IAAI,OAAO,CAAC,CAAC,OAAO;IAClB,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;IAAO,CAAC;IACxC,WAAW,IAAI,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC;GACvC,CAAC;GACD,IAAI,eACF,IAAI,SAAS,CAAC,CAAC,QAAQ,EAAE;EAE7B,OACE,IAAI,eACF,IAAI,SAAS,CAAC,CAAC,QAAQ,MAAM;OACxB;GACL,MAAM,cAAc,IAAI,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC;GAC9C,IACG,SAAS,CAAC,CACV,QAAQ,YAAY,KAAK,IAAI,GAAG,YAAY,GAAG,WAAW,MAAM;EACrE;CAEJ,GAAG;EAAC;EAAK;EAAc;EAAe;CAAM,CAAC;CAE7C,IAAI,UAAU,OAAO;CACrB,OAAO;AACT;;;;;;;;;;;AAiBA,MAAa,6BAA6B,mBACxC,+BACA,sBACA,CAAC,QAAQ,eAAe,CAC1B"}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Primitive } from "../../utils/Primitive.js";
3
- import { forwardRef } from "react";
3
+ import { forwardRef } from "@assistant-ui/tap/react-shim";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/primitives/thread/ThreadRoot.tsx
6
6
  /**
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { useThreadViewport, useThreadViewportStore } from "../../context/react/ThreadViewportContext.js";
3
3
  import { createActionButton } from "../../utils/createActionButton.js";
4
- import { useCallback } from "react";
4
+ import { useCallback } from "@assistant-ui/tap/react-shim";
5
5
  //#region src/primitives/thread/ThreadScrollToBottom.ts
6
6
  const useThreadScrollToBottom = ({ behavior } = {}) => {
7
7
  const isAtBottom = useThreadViewport((s) => s.isAtBottom);
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadScrollToBottom.js","names":[],"sources":["../../../src/primitives/thread/ThreadScrollToBottom.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useCallback } from \"react\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\n\nexport namespace useThreadScrollToBottom {\n export type Options = {\n behavior?: ScrollBehavior | undefined;\n };\n}\n\nconst useThreadScrollToBottom = ({\n behavior,\n}: useThreadScrollToBottom.Options = {}) => {\n const isAtBottom = useThreadViewport((s) => s.isAtBottom);\n\n const threadViewportStore = useThreadViewportStore();\n\n const handleScrollToBottom = useCallback(() => {\n threadViewportStore.getState().scrollToBottom({ behavior });\n }, [threadViewportStore, behavior]);\n\n if (isAtBottom) return null;\n return handleScrollToBottom;\n};\n\nexport namespace ThreadPrimitiveScrollToBottom {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useThreadScrollToBottom>;\n}\n\nexport const ThreadPrimitiveScrollToBottom = createActionButton(\n \"ThreadPrimitive.ScrollToBottom\",\n useThreadScrollToBottom,\n [\"behavior\"],\n);\n"],"mappings":";;;;;AAmBA,MAAM,2BAA2B,EAC/B,aACmC,CAAC,MAAM;CAC1C,MAAM,aAAa,mBAAmB,MAAM,EAAE,UAAU;CAExD,MAAM,sBAAsB,uBAAuB;CAEnD,MAAM,uBAAuB,kBAAkB;EAC7C,oBAAoB,SAAS,EAAE,eAAe,EAAE,SAAS,CAAC;CAC5D,GAAG,CAAC,qBAAqB,QAAQ,CAAC;CAElC,IAAI,YAAY,OAAO;CACvB,OAAO;AACT;AAOA,MAAa,gCAAgC,mBAC3C,kCACA,yBACA,CAAC,UAAU,CACb"}
1
+ {"version":3,"file":"ThreadScrollToBottom.js","names":[],"sources":["../../../src/primitives/thread/ThreadScrollToBottom.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useCallback } from \"react\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\n\nexport namespace useThreadScrollToBottom {\n export type Options = {\n behavior?: ScrollBehavior | undefined;\n };\n}\n\nconst useThreadScrollToBottom = ({\n behavior,\n}: useThreadScrollToBottom.Options = {}) => {\n const isAtBottom = useThreadViewport((s) => s.isAtBottom);\n\n const threadViewportStore = useThreadViewportStore();\n\n const handleScrollToBottom = useCallback(() => {\n threadViewportStore.getState().scrollToBottom({ behavior });\n }, [threadViewportStore, behavior]);\n\n if (isAtBottom) return null;\n return handleScrollToBottom;\n};\n\nexport namespace ThreadPrimitiveScrollToBottom {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useThreadScrollToBottom>;\n}\n\nexport const ThreadPrimitiveScrollToBottom = createActionButton(\n \"ThreadPrimitive.ScrollToBottom\",\n useThreadScrollToBottom,\n [\"behavior\"],\n);\n"],"mappings":";;;;;AAmBA,MAAM,2BAA2B,EAC/B,aACmC,CAAC,MAAM;CAC1C,MAAM,aAAa,mBAAmB,MAAM,EAAE,UAAU;CAExD,MAAM,sBAAsB,uBAAuB;CAEnD,MAAM,uBAAuB,kBAAkB;EAC7C,oBAAoB,SAAS,CAAC,CAAC,eAAe,EAAE,SAAS,CAAC;CAC5D,GAAG,CAAC,qBAAqB,QAAQ,CAAC;CAElC,IAAI,YAAY,OAAO;CACvB,OAAO;AACT;AAOA,MAAa,gCAAgC,mBAC3C,kCACA,yBACA,CAAC,UAAU,CACb"}
@@ -8,7 +8,7 @@ import { useSizeHandle } from "../../utils/hooks/useSizeHandle.js";
8
8
  import { useTopAnchorReserve } from "./topAnchor/useTopAnchorReserve.js";
9
9
  import { getActiveTopAnchorAnchorId, getActiveTopAnchorTargetId } from "./topAnchor/topAnchorTurn.js";
10
10
  import { useAuiEvent, useAuiState } from "@assistant-ui/store";
11
- import { forwardRef, useCallback, useLayoutEffect, useMemo } from "react";
11
+ import { forwardRef, useCallback, useLayoutEffect, useMemo } from "@assistant-ui/tap/react-shim";
12
12
  import { jsx } from "react/jsx-runtime";
13
13
  import { useComposedRefs } from "@radix-ui/react-compose-refs";
14
14
  //#region src/primitives/thread/ThreadViewport.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadViewport.js","names":[],"sources":["../../../src/primitives/thread/ThreadViewport.tsx"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from \"react\";\nimport { useAuiEvent, useAuiState } from \"@assistant-ui/store\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { useThreadViewportAutoScroll } from \"./useThreadViewportAutoScroll\";\nimport { ThreadPrimitiveViewportProvider } from \"../../context/providers/ThreadViewportProvider\";\nimport { useSizeHandle } from \"../../utils/hooks/useSizeHandle\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\nimport { useTopAnchorReserve } from \"./topAnchor/useTopAnchorReserve\";\nimport {\n getActiveTopAnchorAnchorId,\n getActiveTopAnchorTargetId,\n} from \"./topAnchor/topAnchorTurn\";\n\nexport namespace ThreadPrimitiveViewport {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div> & {\n /**\n * Whether to automatically scroll to the bottom when new messages are added.\n * When enabled, the viewport will automatically scroll to show the latest content.\n *\n * Default false if `turnAnchor` is \"top\", otherwise defaults to true.\n */\n autoScroll?: boolean | undefined;\n\n /**\n * Controls scroll anchoring behavior for new messages.\n * - \"bottom\" (default): Messages anchor at the bottom, classic chat behavior.\n * - \"top\": New user messages anchor at the top of the viewport for a focused reading experience.\n */\n turnAnchor?: \"top\" | \"bottom\" | undefined;\n\n /**\n * Clamps tall user messages so the assistant response stays in view.\n *\n * @default { tallerThan: \"10em\", visibleHeight: \"6em\" }\n */\n topAnchorMessageClamp?: {\n /**\n * Clamp messages taller than this. Supports `px`, `em`, and `rem`.\n *\n * @default \"10em\"\n */\n tallerThan?: string;\n /**\n * Visible portion of clamped messages. Supports `px`, `em`, and `rem`.\n *\n * @default \"6em\"\n */\n visibleHeight?: string;\n };\n\n /**\n * Whether to scroll to bottom when a new run starts.\n *\n * Defaults to true.\n */\n scrollToBottomOnRunStart?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when thread history is first loaded.\n *\n * Defaults to true.\n */\n scrollToBottomOnInitialize?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when switching to a different thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n}\n\nconst useViewportSizeRef = () => {\n const register = useThreadViewport((s) => s.registerViewport);\n const getHeight = useCallback((el: HTMLElement) => el.clientHeight, []);\n return useSizeHandle(register, getHeight);\n};\n\nconst useViewportElementRef = () => {\n const registerViewportElement = useThreadViewport(\n (s) => s.registerViewportElement,\n );\n\n return useManagedRef(registerViewportElement);\n};\n\nconst useTopAnchorTurn = (enabled: boolean) => {\n const threadViewportStore = useThreadViewportStore();\n const activeAnchorId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorAnchorId(s.thread);\n });\n const activeTargetId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorTargetId(s.thread);\n });\n const activeTurn = useMemo(() => {\n if (!activeAnchorId || !activeTargetId) return null;\n return { anchorId: activeAnchorId, targetId: activeTargetId };\n }, [activeAnchorId, activeTargetId]);\n\n useLayoutEffect(() => {\n if (!activeTurn) return;\n\n const state = threadViewportStore.getState();\n const current = state.topAnchorTurn;\n if (\n current?.anchorId === activeTurn.anchorId &&\n current.targetId === activeTurn.targetId\n ) {\n return;\n }\n\n state.setTopAnchorTurn(activeTurn);\n }, [activeTurn, threadViewportStore]);\n\n const clearTopAnchorTurn = useCallback(() => {\n threadViewportStore.getState().setTopAnchorTurn(null);\n }, [threadViewportStore]);\n\n useAuiEvent(\"thread.initialize\", clearTopAnchorTurn);\n useAuiEvent(\"threadListItem.switchedTo\", clearTopAnchorTurn);\n};\n\nconst ThreadPrimitiveViewportScrollable = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(\n (\n {\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n children,\n ...rest\n },\n forwardedRef,\n ) => {\n const autoScrollRef = useThreadViewportAutoScroll<HTMLDivElement>({\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n });\n const viewportSizeRef = useViewportSizeRef();\n const viewportElementRef = useViewportElementRef();\n const threadViewportStore = useThreadViewportStore();\n const turnAnchor = threadViewportStore.getState().turnAnchor;\n const topAnchorEnabled = turnAnchor === \"top\";\n useTopAnchorTurn(topAnchorEnabled);\n useTopAnchorReserve(topAnchorEnabled);\n const ref = useComposedRefs(\n forwardedRef,\n autoScrollRef,\n viewportSizeRef,\n viewportElementRef,\n );\n\n return (\n <Primitive.div {...rest} ref={ref}>\n {children}\n </Primitive.div>\n );\n },\n);\n\nThreadPrimitiveViewportScrollable.displayName =\n \"ThreadPrimitive.ViewportScrollable\";\n\n/**\n * A scrollable viewport container for thread messages.\n *\n * This component provides a scrollable area for displaying thread messages with\n * automatic scrolling capabilities. It manages the viewport state and provides\n * context for child components to access viewport-related functionality.\n *\n * @example\n * ```tsx\n * <ThreadPrimitive.Viewport turnAnchor=\"top\">\n * <ThreadPrimitive.Messages>\n * {() => <MyMessage />}\n * </ThreadPrimitive.Messages>\n * </ThreadPrimitive.Viewport>\n * ```\n */\nexport const ThreadPrimitiveViewport = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(({ turnAnchor, topAnchorMessageClamp, ...props }, ref) => {\n return (\n <ThreadPrimitiveViewportProvider\n options={{ turnAnchor, topAnchorMessageClamp }}\n >\n <ThreadPrimitiveViewportScrollable {...props} ref={ref} />\n </ThreadPrimitiveViewportProvider>\n );\n});\n\nThreadPrimitiveViewport.displayName = \"ThreadPrimitive.Viewport\";\n"],"mappings":";;;;;;;;;;;;;;AAwFA,MAAM,2BAA2B;CAG/B,OAAO,cAFU,mBAAmB,MAAM,EAAE,gBAEhB,GADV,aAAa,OAAoB,GAAG,cAAc,CAAC,CAC9B,CAAC;AAC1C;AAEA,MAAM,8BAA8B;CAKlC,OAAO,cAJyB,mBAC7B,MAAM,EAAE,uBAGgC,CAAC;AAC9C;AAEA,MAAM,oBAAoB,YAAqB;CAC7C,MAAM,sBAAsB,uBAAuB;CACnD,MAAM,iBAAiB,aAAa,MAAM;EACxC,IAAI,CAAC,SAAS,OAAO,KAAA;EACrB,OAAO,2BAA2B,EAAE,MAAM;CAC5C,CAAC;CACD,MAAM,iBAAiB,aAAa,MAAM;EACxC,IAAI,CAAC,SAAS,OAAO,KAAA;EACrB,OAAO,2BAA2B,EAAE,MAAM;CAC5C,CAAC;CACD,MAAM,aAAa,cAAc;EAC/B,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,OAAO;EAC/C,OAAO;GAAE,UAAU;GAAgB,UAAU;EAAe;CAC9D,GAAG,CAAC,gBAAgB,cAAc,CAAC;CAEnC,sBAAsB;EACpB,IAAI,CAAC,YAAY;EAEjB,MAAM,QAAQ,oBAAoB,SAAS;EAC3C,MAAM,UAAU,MAAM;EACtB,IACE,SAAS,aAAa,WAAW,YACjC,QAAQ,aAAa,WAAW,UAEhC;EAGF,MAAM,iBAAiB,UAAU;CACnC,GAAG,CAAC,YAAY,mBAAmB,CAAC;CAEpC,MAAM,qBAAqB,kBAAkB;EAC3C,oBAAoB,SAAS,EAAE,iBAAiB,IAAI;CACtD,GAAG,CAAC,mBAAmB,CAAC;CAExB,YAAY,qBAAqB,kBAAkB;CACnD,YAAY,6BAA6B,kBAAkB;AAC7D;AAEA,MAAM,oCAAoC,YAKtC,EACE,YACA,0BACA,4BACA,8BACA,UACA,GAAG,QAEL,iBACG;CACH,MAAM,gBAAgB,4BAA4C;EAChE;EACA;EACA;EACA;CACF,CAAC;CACD,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,qBAAqB,sBAAsB;CAGjD,MAAM,mBAFsB,uBACS,EAAE,SAAS,EAAE,eACV;CACxC,iBAAiB,gBAAgB;CACjC,oBAAoB,gBAAgB;CACpC,MAAM,MAAM,gBACV,cACA,eACA,iBACA,kBACF;CAEA,OACE,oBAAC,UAAU,KAAX;EAAe,GAAI;EAAW;EAC3B;CACY,CAAA;AAEnB,CACF;AAEA,kCAAkC,cAChC;;;;;;;;;;;;;;;;;AAkBF,MAAa,0BAA0B,YAGpC,EAAE,YAAY,uBAAuB,GAAG,SAAS,QAAQ;CAC1D,OACE,oBAAC,iCAAD;EACE,SAAS;GAAE;GAAY;EAAsB;YAE7C,oBAAC,mCAAD;GAAmC,GAAI;GAAY;EAAM,CAAA;CAC1B,CAAA;AAErC,CAAC;AAED,wBAAwB,cAAc"}
1
+ {"version":3,"file":"ThreadViewport.js","names":[],"sources":["../../../src/primitives/thread/ThreadViewport.tsx"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from \"react\";\nimport { useAuiEvent, useAuiState } from \"@assistant-ui/store\";\nimport { useManagedRef } from \"../../utils/hooks/useManagedRef\";\nimport { useThreadViewportAutoScroll } from \"./useThreadViewportAutoScroll\";\nimport { ThreadPrimitiveViewportProvider } from \"../../context/providers/ThreadViewportProvider\";\nimport { useSizeHandle } from \"../../utils/hooks/useSizeHandle\";\nimport {\n useThreadViewport,\n useThreadViewportStore,\n} from \"../../context/react/ThreadViewportContext\";\nimport { useTopAnchorReserve } from \"./topAnchor/useTopAnchorReserve\";\nimport {\n getActiveTopAnchorAnchorId,\n getActiveTopAnchorTargetId,\n} from \"./topAnchor/topAnchorTurn\";\n\nexport namespace ThreadPrimitiveViewport {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div> & {\n /**\n * Whether to automatically scroll to the bottom when new messages are added.\n * When enabled, the viewport will automatically scroll to show the latest content.\n *\n * Default false if `turnAnchor` is \"top\", otherwise defaults to true.\n */\n autoScroll?: boolean | undefined;\n\n /**\n * Controls scroll anchoring behavior for new messages.\n * - \"bottom\" (default): Messages anchor at the bottom, classic chat behavior.\n * - \"top\": New user messages anchor at the top of the viewport for a focused reading experience.\n */\n turnAnchor?: \"top\" | \"bottom\" | undefined;\n\n /**\n * Clamps tall user messages so the assistant response stays in view.\n *\n * @default { tallerThan: \"10em\", visibleHeight: \"6em\" }\n */\n topAnchorMessageClamp?: {\n /**\n * Clamp messages taller than this. Supports `px`, `em`, and `rem`.\n *\n * @default \"10em\"\n */\n tallerThan?: string;\n /**\n * Visible portion of clamped messages. Supports `px`, `em`, and `rem`.\n *\n * @default \"6em\"\n */\n visibleHeight?: string;\n };\n\n /**\n * Whether to scroll to bottom when a new run starts.\n *\n * Defaults to true.\n */\n scrollToBottomOnRunStart?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when thread history is first loaded.\n *\n * Defaults to true.\n */\n scrollToBottomOnInitialize?: boolean | undefined;\n\n /**\n * Whether to scroll to bottom when switching to a different thread.\n *\n * Defaults to true.\n */\n scrollToBottomOnThreadSwitch?: boolean | undefined;\n };\n}\n\nconst useViewportSizeRef = () => {\n const register = useThreadViewport((s) => s.registerViewport);\n const getHeight = useCallback((el: HTMLElement) => el.clientHeight, []);\n return useSizeHandle(register, getHeight);\n};\n\nconst useViewportElementRef = () => {\n const registerViewportElement = useThreadViewport(\n (s) => s.registerViewportElement,\n );\n\n return useManagedRef(registerViewportElement);\n};\n\nconst useTopAnchorTurn = (enabled: boolean) => {\n const threadViewportStore = useThreadViewportStore();\n const activeAnchorId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorAnchorId(s.thread);\n });\n const activeTargetId = useAuiState((s) => {\n if (!enabled) return undefined;\n return getActiveTopAnchorTargetId(s.thread);\n });\n const activeTurn = useMemo(() => {\n if (!activeAnchorId || !activeTargetId) return null;\n return { anchorId: activeAnchorId, targetId: activeTargetId };\n }, [activeAnchorId, activeTargetId]);\n\n useLayoutEffect(() => {\n if (!activeTurn) return;\n\n const state = threadViewportStore.getState();\n const current = state.topAnchorTurn;\n if (\n current?.anchorId === activeTurn.anchorId &&\n current.targetId === activeTurn.targetId\n ) {\n return;\n }\n\n state.setTopAnchorTurn(activeTurn);\n }, [activeTurn, threadViewportStore]);\n\n const clearTopAnchorTurn = useCallback(() => {\n threadViewportStore.getState().setTopAnchorTurn(null);\n }, [threadViewportStore]);\n\n useAuiEvent(\"thread.initialize\", clearTopAnchorTurn);\n useAuiEvent(\"threadListItem.switchedTo\", clearTopAnchorTurn);\n};\n\nconst ThreadPrimitiveViewportScrollable = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(\n (\n {\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n children,\n ...rest\n },\n forwardedRef,\n ) => {\n const autoScrollRef = useThreadViewportAutoScroll<HTMLDivElement>({\n autoScroll,\n scrollToBottomOnRunStart,\n scrollToBottomOnInitialize,\n scrollToBottomOnThreadSwitch,\n });\n const viewportSizeRef = useViewportSizeRef();\n const viewportElementRef = useViewportElementRef();\n const threadViewportStore = useThreadViewportStore();\n const turnAnchor = threadViewportStore.getState().turnAnchor;\n const topAnchorEnabled = turnAnchor === \"top\";\n useTopAnchorTurn(topAnchorEnabled);\n useTopAnchorReserve(topAnchorEnabled);\n const ref = useComposedRefs(\n forwardedRef,\n autoScrollRef,\n viewportSizeRef,\n viewportElementRef,\n );\n\n return (\n <Primitive.div {...rest} ref={ref}>\n {children}\n </Primitive.div>\n );\n },\n);\n\nThreadPrimitiveViewportScrollable.displayName =\n \"ThreadPrimitive.ViewportScrollable\";\n\n/**\n * A scrollable viewport container for thread messages.\n *\n * This component provides a scrollable area for displaying thread messages with\n * automatic scrolling capabilities. It manages the viewport state and provides\n * context for child components to access viewport-related functionality.\n *\n * @example\n * ```tsx\n * <ThreadPrimitive.Viewport turnAnchor=\"top\">\n * <ThreadPrimitive.Messages>\n * {() => <MyMessage />}\n * </ThreadPrimitive.Messages>\n * </ThreadPrimitive.Viewport>\n * ```\n */\nexport const ThreadPrimitiveViewport = forwardRef<\n ThreadPrimitiveViewport.Element,\n ThreadPrimitiveViewport.Props\n>(({ turnAnchor, topAnchorMessageClamp, ...props }, ref) => {\n return (\n <ThreadPrimitiveViewportProvider\n options={{ turnAnchor, topAnchorMessageClamp }}\n >\n <ThreadPrimitiveViewportScrollable {...props} ref={ref} />\n </ThreadPrimitiveViewportProvider>\n );\n});\n\nThreadPrimitiveViewport.displayName = \"ThreadPrimitive.Viewport\";\n"],"mappings":";;;;;;;;;;;;;;AAwFA,MAAM,2BAA2B;CAG/B,OAAO,cAFU,mBAAmB,MAAM,EAAE,gBAEhB,GADV,aAAa,OAAoB,GAAG,cAAc,CAAC,CAC9B,CAAC;AAC1C;AAEA,MAAM,8BAA8B;CAKlC,OAAO,cAJyB,mBAC7B,MAAM,EAAE,uBAGgC,CAAC;AAC9C;AAEA,MAAM,oBAAoB,YAAqB;CAC7C,MAAM,sBAAsB,uBAAuB;CACnD,MAAM,iBAAiB,aAAa,MAAM;EACxC,IAAI,CAAC,SAAS,OAAO,KAAA;EACrB,OAAO,2BAA2B,EAAE,MAAM;CAC5C,CAAC;CACD,MAAM,iBAAiB,aAAa,MAAM;EACxC,IAAI,CAAC,SAAS,OAAO,KAAA;EACrB,OAAO,2BAA2B,EAAE,MAAM;CAC5C,CAAC;CACD,MAAM,aAAa,cAAc;EAC/B,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,OAAO;EAC/C,OAAO;GAAE,UAAU;GAAgB,UAAU;EAAe;CAC9D,GAAG,CAAC,gBAAgB,cAAc,CAAC;CAEnC,sBAAsB;EACpB,IAAI,CAAC,YAAY;EAEjB,MAAM,QAAQ,oBAAoB,SAAS;EAC3C,MAAM,UAAU,MAAM;EACtB,IACE,SAAS,aAAa,WAAW,YACjC,QAAQ,aAAa,WAAW,UAEhC;EAGF,MAAM,iBAAiB,UAAU;CACnC,GAAG,CAAC,YAAY,mBAAmB,CAAC;CAEpC,MAAM,qBAAqB,kBAAkB;EAC3C,oBAAoB,SAAS,CAAC,CAAC,iBAAiB,IAAI;CACtD,GAAG,CAAC,mBAAmB,CAAC;CAExB,YAAY,qBAAqB,kBAAkB;CACnD,YAAY,6BAA6B,kBAAkB;AAC7D;AAEA,MAAM,oCAAoC,YAKtC,EACE,YACA,0BACA,4BACA,8BACA,UACA,GAAG,QAEL,iBACG;CACH,MAAM,gBAAgB,4BAA4C;EAChE;EACA;EACA;EACA;CACF,CAAC;CACD,MAAM,kBAAkB,mBAAmB;CAC3C,MAAM,qBAAqB,sBAAsB;CAGjD,MAAM,mBAFsB,uBACS,CAAC,CAAC,SAAS,CAAC,CAAC,eACV;CACxC,iBAAiB,gBAAgB;CACjC,oBAAoB,gBAAgB;CACpC,MAAM,MAAM,gBACV,cACA,eACA,iBACA,kBACF;CAEA,OACE,oBAAC,UAAU,KAAX;EAAe,GAAI;EAAW;EAC3B;CACY,CAAA;AAEnB,CACF;AAEA,kCAAkC,cAChC;;;;;;;;;;;;;;;;;AAkBF,MAAa,0BAA0B,YAGpC,EAAE,YAAY,uBAAuB,GAAG,SAAS,QAAQ;CAC1D,OACE,oBAAC,iCAAD;EACE,SAAS;GAAE;GAAY;EAAsB;YAE7C,oBAAC,mCAAD;GAAmC,GAAI;GAAY;EAAM,CAAA;CAC1B,CAAA;AAErC,CAAC;AAED,wBAAwB,cAAc"}
@@ -2,7 +2,7 @@
2
2
  import { useThreadViewport } from "../../context/react/ThreadViewportContext.js";
3
3
  import { Primitive } from "../../utils/Primitive.js";
4
4
  import { useSizeHandle } from "../../utils/hooks/useSizeHandle.js";
5
- import { forwardRef, useCallback } from "react";
5
+ import { forwardRef, useCallback } from "@assistant-ui/tap/react-shim";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { useComposedRefs } from "@radix-ui/react-compose-refs";
8
8
  //#region src/primitives/thread/ThreadViewportFooter.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"ThreadViewportFooter.js","names":[],"sources":["../../../src/primitives/thread/ThreadViewportFooter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n useCallback,\n} from \"react\";\nimport { useSizeHandle } from \"../../utils/hooks/useSizeHandle\";\nimport { useThreadViewport } from \"../../context/react/ThreadViewportContext\";\n\nexport namespace ThreadPrimitiveViewportFooter {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\n/**\n * A footer container that measures its height for scroll calculations.\n *\n * This component measures its height and provides it to the viewport context\n * so the auto-scroll system can account for any sticky footer overlapping the\n * message list.\n *\n * Multiple ViewportFooter components can be used - their heights are summed.\n *\n * Typically used with `className=\"sticky bottom-0\"` to keep the footer\n * visible at the bottom of the viewport while scrolling.\n *\n * @example\n * ```tsx\n * <ThreadPrimitive.Viewport>\n * <ThreadPrimitive.Messages>\n * {() => <MyMessage />}\n * </ThreadPrimitive.Messages>\n * <ThreadPrimitive.ViewportFooter className=\"sticky bottom-0\">\n * <Composer />\n * </ThreadPrimitive.ViewportFooter>\n * </ThreadPrimitive.Viewport>\n * ```\n */\nexport const ThreadPrimitiveViewportFooter = forwardRef<\n ThreadPrimitiveViewportFooter.Element,\n ThreadPrimitiveViewportFooter.Props\n>((props, forwardedRef) => {\n const register = useThreadViewport((s) => s.registerContentInset);\n const getHeight = useCallback((el: HTMLElement) => {\n const marginTop = parseFloat(getComputedStyle(el).marginTop) || 0;\n return el.offsetHeight + marginTop;\n }, []);\n\n const resizeRef = useSizeHandle(register, getHeight);\n\n const ref = useComposedRefs(forwardedRef, resizeRef);\n\n return <Primitive.div {...props} ref={ref} />;\n});\n\nThreadPrimitiveViewportFooter.displayName = \"ThreadPrimitive.ViewportFooter\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,gCAAgC,YAG1C,OAAO,iBAAiB;CASzB,MAAM,MAAM,gBAAgB,cAFV,cAND,mBAAmB,MAAM,EAAE,oBAML,GALrB,aAAa,OAAoB;EACjD,MAAM,YAAY,WAAW,iBAAiB,EAAE,EAAE,SAAS,KAAK;EAChE,OAAO,GAAG,eAAe;CAC3B,GAAG,CAAC,CAE8C,CAEA,CAAC;CAEnD,OAAO,oBAAC,UAAU,KAAX;EAAe,GAAI;EAAY;CAAM,CAAA;AAC9C,CAAC;AAED,8BAA8B,cAAc"}
1
+ {"version":3,"file":"ThreadViewportFooter.js","names":[],"sources":["../../../src/primitives/thread/ThreadViewportFooter.tsx"],"sourcesContent":["\"use client\";\n\nimport { useComposedRefs } from \"@radix-ui/react-compose-refs\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n useCallback,\n} from \"react\";\nimport { useSizeHandle } from \"../../utils/hooks/useSizeHandle\";\nimport { useThreadViewport } from \"../../context/react/ThreadViewportContext\";\n\nexport namespace ThreadPrimitiveViewportFooter {\n export type Element = ComponentRef<typeof Primitive.div>;\n export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;\n}\n\n/**\n * A footer container that measures its height for scroll calculations.\n *\n * This component measures its height and provides it to the viewport context\n * so the auto-scroll system can account for any sticky footer overlapping the\n * message list.\n *\n * Multiple ViewportFooter components can be used - their heights are summed.\n *\n * Typically used with `className=\"sticky bottom-0\"` to keep the footer\n * visible at the bottom of the viewport while scrolling.\n *\n * @example\n * ```tsx\n * <ThreadPrimitive.Viewport>\n * <ThreadPrimitive.Messages>\n * {() => <MyMessage />}\n * </ThreadPrimitive.Messages>\n * <ThreadPrimitive.ViewportFooter className=\"sticky bottom-0\">\n * <Composer />\n * </ThreadPrimitive.ViewportFooter>\n * </ThreadPrimitive.Viewport>\n * ```\n */\nexport const ThreadPrimitiveViewportFooter = forwardRef<\n ThreadPrimitiveViewportFooter.Element,\n ThreadPrimitiveViewportFooter.Props\n>((props, forwardedRef) => {\n const register = useThreadViewport((s) => s.registerContentInset);\n const getHeight = useCallback((el: HTMLElement) => {\n const marginTop = parseFloat(getComputedStyle(el).marginTop) || 0;\n return el.offsetHeight + marginTop;\n }, []);\n\n const resizeRef = useSizeHandle(register, getHeight);\n\n const ref = useComposedRefs(forwardedRef, resizeRef);\n\n return <Primitive.div {...props} ref={ref} />;\n});\n\nThreadPrimitiveViewportFooter.displayName = \"ThreadPrimitive.ViewportFooter\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,gCAAgC,YAG1C,OAAO,iBAAiB;CASzB,MAAM,MAAM,gBAAgB,cAFV,cAND,mBAAmB,MAAM,EAAE,oBAML,GALrB,aAAa,OAAoB;EACjD,MAAM,YAAY,WAAW,iBAAiB,EAAE,CAAC,CAAC,SAAS,KAAK;EAChE,OAAO,GAAG,eAAe;CAC3B,GAAG,CAAC,CAE8C,CAEA,CAAC;CAEnD,OAAO,oBAAC,UAAU,KAAX;EAAe,GAAI;EAAY;CAAM,CAAA;AAC9C,CAAC;AAED,8BAA8B,cAAc"}
@@ -1 +1 @@
1
- {"version":3,"file":"topAnchorTurn.js","names":[],"sources":["../../../../src/primitives/thread/topAnchor/topAnchorTurn.ts"],"sourcesContent":["\"use client\";\n\ntype TopAnchorTurnMessage = {\n readonly id: string;\n readonly role: string;\n};\n\nexport const getActiveTopAnchorTurn = ({\n isRunning,\n messages,\n}: {\n readonly isRunning: boolean;\n readonly messages: readonly TopAnchorTurnMessage[];\n}) => {\n if (!isRunning) return null;\n\n const target = messages.at(-1);\n const anchor = messages.at(-2);\n if (anchor?.role !== \"user\" || target?.role !== \"assistant\") return null;\n\n return { anchorId: anchor.id, targetId: target.id };\n};\n\nexport const getActiveTopAnchorAnchorId = (\n options: Parameters<typeof getActiveTopAnchorTurn>[0],\n) => getActiveTopAnchorTurn(options)?.anchorId;\n\nexport const getActiveTopAnchorTargetId = (\n options: Parameters<typeof getActiveTopAnchorTurn>[0],\n) => getActiveTopAnchorTurn(options)?.targetId;\n"],"mappings":";;AAOA,MAAa,0BAA0B,EACrC,WACA,eAII;CACJ,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,SAAS,SAAS,GAAG,EAAE;CAC7B,MAAM,SAAS,SAAS,GAAG,EAAE;CAC7B,IAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,aAAa,OAAO;CAEpE,OAAO;EAAE,UAAU,OAAO;EAAI,UAAU,OAAO;CAAG;AACpD;AAEA,MAAa,8BACX,YACG,uBAAuB,OAAO,GAAG;AAEtC,MAAa,8BACX,YACG,uBAAuB,OAAO,GAAG"}
1
+ {"version":3,"file":"topAnchorTurn.js","names":[],"sources":["../../../../src/primitives/thread/topAnchor/topAnchorTurn.ts"],"sourcesContent":["\"use client\";\n\ntype TopAnchorTurnMessage = {\n readonly id: string;\n readonly role: string;\n};\n\nexport const getActiveTopAnchorTurn = ({\n isRunning,\n messages,\n}: {\n readonly isRunning: boolean;\n readonly messages: readonly TopAnchorTurnMessage[];\n}) => {\n if (!isRunning) return null;\n\n const target = messages.at(-1);\n const anchor = messages.at(-2);\n if (anchor?.role !== \"user\" || target?.role !== \"assistant\") return null;\n\n return { anchorId: anchor.id, targetId: target.id };\n};\n\nexport const getActiveTopAnchorAnchorId = (\n options: Parameters<typeof getActiveTopAnchorTurn>[0],\n) => getActiveTopAnchorTurn(options)?.anchorId;\n\nexport const getActiveTopAnchorTargetId = (\n options: Parameters<typeof getActiveTopAnchorTurn>[0],\n) => getActiveTopAnchorTurn(options)?.targetId;\n"],"mappings":";;AAOA,MAAa,0BAA0B,EACrC,WACA,eAII;CACJ,IAAI,CAAC,WAAW,OAAO;CAEvB,MAAM,SAAS,SAAS,GAAG,EAAE;CAC7B,MAAM,SAAS,SAAS,GAAG,EAAE;CAC7B,IAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,aAAa,OAAO;CAEpE,OAAO;EAAE,UAAU,OAAO;EAAI,UAAU,OAAO;CAAG;AACpD;AAEA,MAAa,8BACX,YACG,uBAAuB,OAAO,CAAC,EAAE;AAEtC,MAAa,8BACX,YACG,uBAAuB,OAAO,CAAC,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"topAnchorUtils.js","names":[],"sources":["../../../../src/primitives/thread/topAnchor/topAnchorUtils.ts"],"sourcesContent":["\"use client\";\n\n/**\n * Convert a supported CSS length string (`px`, `em`, `rem`) into pixels,\n * resolving font-relative units against the supplied element's computed style.\n * Unsupported or malformed values disable the tall-message clamp.\n *\n * Part of the top-anchor package's public input contract: consumers may pass\n * clamp configuration as supported CSS-length strings, and this function is the\n * single place that converts them into the pixel values the package operates on.\n */\nexport const parseCssLength = (value: string, element: HTMLElement): number => {\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?|\\.\\d+)(em|px|rem)$/);\n if (!match) return Number.POSITIVE_INFINITY;\n\n const num = Number(match[1]);\n const unit = match[2];\n\n if (unit === \"px\") return num;\n if (unit === \"em\") {\n const fontSize = parseFloat(getComputedStyle(element).fontSize) || 16;\n return num * fontSize;\n }\n if (unit === \"rem\") {\n const rootFontSize =\n parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n return num * rootFontSize;\n }\n return Number.POSITIVE_INFINITY;\n};\n\nexport const getAnchorId = (anchor: HTMLElement) => anchor.dataset.messageId;\n\nexport const createReserveElement = () => {\n const reserve = document.createElement(\"div\");\n reserve.dataset.auiTopAnchorReserve = \"\";\n reserve.style.height = \"0px\";\n reserve.style.flexShrink = \"0\";\n reserve.style.pointerEvents = \"none\";\n reserve.setAttribute(\"aria-hidden\", \"true\");\n\n return reserve;\n};\n\nexport const setReserveHeight = (reserve: HTMLElement, height: number) => {\n const nextHeight = `${height}px`;\n if (reserve.style.height !== nextHeight) {\n reserve.style.height = nextHeight;\n return true;\n }\n\n return false;\n};\n\nexport const snapScrollTop = (top: number) => {\n const pixelRatio = window.devicePixelRatio || 1;\n return Math.round(top * pixelRatio) / pixelRatio;\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,kBAAkB,OAAe,YAAiC;CAC7E,MAAM,QAAQ,MAAM,KAAK,EAAE,MAAM,oCAAoC;CACrE,IAAI,CAAC,OAAO,OAAO,OAAO;CAE1B,MAAM,MAAM,OAAO,MAAM,EAAE;CAC3B,MAAM,OAAO,MAAM;CAEnB,IAAI,SAAS,MAAM,OAAO;CAC1B,IAAI,SAAS,MAEX,OAAO,OADU,WAAW,iBAAiB,OAAO,EAAE,QAAQ,KAAK;CAGrE,IAAI,SAAS,OAGX,OAAO,OADL,WAAW,iBAAiB,SAAS,eAAe,EAAE,QAAQ,KAAK;CAGvE,OAAO,OAAO;AAChB;AAEA,MAAa,eAAe,WAAwB,OAAO,QAAQ;AAEnE,MAAa,6BAA6B;CACxC,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,QAAQ,sBAAsB;CACtC,QAAQ,MAAM,SAAS;CACvB,QAAQ,MAAM,aAAa;CAC3B,QAAQ,MAAM,gBAAgB;CAC9B,QAAQ,aAAa,eAAe,MAAM;CAE1C,OAAO;AACT;AAEA,MAAa,oBAAoB,SAAsB,WAAmB;CACxE,MAAM,aAAa,GAAG,OAAO;CAC7B,IAAI,QAAQ,MAAM,WAAW,YAAY;EACvC,QAAQ,MAAM,SAAS;EACvB,OAAO;CACT;CAEA,OAAO;AACT;AAEA,MAAa,iBAAiB,QAAgB;CAC5C,MAAM,aAAa,OAAO,oBAAoB;CAC9C,OAAO,KAAK,MAAM,MAAM,UAAU,IAAI;AACxC"}
1
+ {"version":3,"file":"topAnchorUtils.js","names":[],"sources":["../../../../src/primitives/thread/topAnchor/topAnchorUtils.ts"],"sourcesContent":["\"use client\";\n\n/**\n * Convert a supported CSS length string (`px`, `em`, `rem`) into pixels,\n * resolving font-relative units against the supplied element's computed style.\n * Unsupported or malformed values disable the tall-message clamp.\n *\n * Part of the top-anchor package's public input contract: consumers may pass\n * clamp configuration as supported CSS-length strings, and this function is the\n * single place that converts them into the pixel values the package operates on.\n */\nexport const parseCssLength = (value: string, element: HTMLElement): number => {\n const match = value.trim().match(/^(\\d+(?:\\.\\d+)?|\\.\\d+)(em|px|rem)$/);\n if (!match) return Number.POSITIVE_INFINITY;\n\n const num = Number(match[1]);\n const unit = match[2];\n\n if (unit === \"px\") return num;\n if (unit === \"em\") {\n const fontSize = parseFloat(getComputedStyle(element).fontSize) || 16;\n return num * fontSize;\n }\n if (unit === \"rem\") {\n const rootFontSize =\n parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n return num * rootFontSize;\n }\n return Number.POSITIVE_INFINITY;\n};\n\nexport const getAnchorId = (anchor: HTMLElement) => anchor.dataset.messageId;\n\nexport const createReserveElement = () => {\n const reserve = document.createElement(\"div\");\n reserve.dataset.auiTopAnchorReserve = \"\";\n reserve.style.height = \"0px\";\n reserve.style.flexShrink = \"0\";\n reserve.style.pointerEvents = \"none\";\n reserve.setAttribute(\"aria-hidden\", \"true\");\n\n return reserve;\n};\n\nexport const setReserveHeight = (reserve: HTMLElement, height: number) => {\n const nextHeight = `${height}px`;\n if (reserve.style.height !== nextHeight) {\n reserve.style.height = nextHeight;\n return true;\n }\n\n return false;\n};\n\nexport const snapScrollTop = (top: number) => {\n const pixelRatio = window.devicePixelRatio || 1;\n return Math.round(top * pixelRatio) / pixelRatio;\n};\n"],"mappings":";;;;;;;;;;;AAWA,MAAa,kBAAkB,OAAe,YAAiC;CAC7E,MAAM,QAAQ,MAAM,KAAK,CAAC,CAAC,MAAM,oCAAoC;CACrE,IAAI,CAAC,OAAO,OAAO,OAAO;CAE1B,MAAM,MAAM,OAAO,MAAM,EAAE;CAC3B,MAAM,OAAO,MAAM;CAEnB,IAAI,SAAS,MAAM,OAAO;CAC1B,IAAI,SAAS,MAEX,OAAO,OADU,WAAW,iBAAiB,OAAO,CAAC,CAAC,QAAQ,KAAK;CAGrE,IAAI,SAAS,OAGX,OAAO,OADL,WAAW,iBAAiB,SAAS,eAAe,CAAC,CAAC,QAAQ,KAAK;CAGvE,OAAO,OAAO;AAChB;AAEA,MAAa,eAAe,WAAwB,OAAO,QAAQ;AAEnE,MAAa,6BAA6B;CACxC,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,QAAQ,sBAAsB;CACtC,QAAQ,MAAM,SAAS;CACvB,QAAQ,MAAM,aAAa;CAC3B,QAAQ,MAAM,gBAAgB;CAC9B,QAAQ,aAAa,eAAe,MAAM;CAE1C,OAAO;AACT;AAEA,MAAa,oBAAoB,SAAsB,WAAmB;CACxE,MAAM,aAAa,GAAG,OAAO;CAC7B,IAAI,QAAQ,MAAM,WAAW,YAAY;EACvC,QAAQ,MAAM,SAAS;EACvB,OAAO;CACT;CAEA,OAAO;AACT;AAEA,MAAa,iBAAiB,QAAgB;CAC5C,MAAM,aAAa,OAAO,oBAAoB;CAC9C,OAAO,KAAK,MAAM,MAAM,UAAU,IAAI;AACxC"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { useThreadViewportStore } from "../../../context/react/ThreadViewportContext.js";
3
3
  import { mountTopAnchorReserve } from "./mountTopAnchorReserve.js";
4
- import { useLayoutEffect } from "react";
4
+ import { useLayoutEffect } from "@assistant-ui/tap/react-shim";
5
5
  //#region src/primitives/thread/topAnchor/useTopAnchorReserve.ts
6
6
  /**
7
7
  * Mounts the top-turn-anchor reserve element against the active
@@ -5,7 +5,7 @@ import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom.js";
5
5
  import { useManagedRef } from "../../utils/hooks/useManagedRef.js";
6
6
  import { useOnResizeContent } from "../../utils/hooks/useOnResizeContent.js";
7
7
  import { useAuiEvent, useAuiState } from "@assistant-ui/store";
8
- import { useCallback, useLayoutEffect, useRef } from "react";
8
+ import { useCallback, useLayoutEffect, useRef } from "@assistant-ui/tap/react-shim";
9
9
  import { useComposedRefs } from "@radix-ui/react-compose-refs";
10
10
  //#region src/primitives/thread/useThreadViewportAutoScroll.ts
11
11
  const useThreadViewportAutoScroll = ({ autoScroll, scrollToBottomOnRunStart = true, scrollToBottomOnInitialize = true, scrollToBottomOnThreadSwitch = true }) => {