@assistant-ui/react 0.14.28 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/client/ExternalThread.d.ts +11 -1
  2. package/dist/client/ExternalThread.d.ts.map +1 -1
  3. package/dist/client/ExternalThread.js +762 -516
  4. package/dist/client/ExternalThread.js.map +1 -1
  5. package/dist/client/InMemoryThreadList.js +3 -3
  6. package/dist/client/InMemoryThreadList.js.map +1 -1
  7. package/dist/context/index.d.ts +1 -8
  8. package/dist/context/index.js +1 -8
  9. package/dist/index.d.ts +1 -8
  10. package/dist/index.js +1 -8
  11. package/dist/legacy-runtime/cloud/auiV0.d.ts +12 -1
  12. package/dist/legacy-runtime/cloud/auiV0.d.ts.map +1 -1
  13. package/dist/legacy-runtime/cloud/auiV0.js +2 -1
  14. package/dist/legacy-runtime/cloud/auiV0.js.map +1 -1
  15. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.d.ts +1 -1
  16. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js +7 -6
  17. package/dist/legacy-runtime/runtime-cores/assistant-transport/commandQueue.js.map +1 -1
  18. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
  19. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +30 -15
  20. package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
  21. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
  22. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +11 -7
  23. package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
  24. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js +1 -0
  25. package/dist/legacy-runtime/runtime-cores/assistant-transport/useConvertedState.js.map +1 -1
  26. package/dist/mcp-apps/McpAppRenderer.js +1 -1
  27. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  28. package/dist/model-context/makeAssistantVisible.js +26 -25
  29. package/dist/model-context/makeAssistantVisible.js.map +1 -1
  30. package/dist/primitives/actionBar/ActionBarExportMarkdown.js +3 -3
  31. package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
  32. package/dist/primitives/actionBar/ActionBarRoot.js +3 -3
  33. package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
  34. package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts +7 -6
  35. package/dist/primitives/actionBar/useActionBarFloatStatus.d.ts.map +1 -1
  36. package/dist/primitives/actionBar/useActionBarFloatStatus.js +10 -11
  37. package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
  38. package/dist/primitives/attachment/AttachmentRemove.js +4 -3
  39. package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
  40. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js +4 -3
  41. package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
  42. package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
  43. package/dist/primitives/composer/ComposerAddAttachment.js +11 -5
  44. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  45. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
  46. package/dist/primitives/composer/ComposerAttachmentDropzone.js +2 -4
  47. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  48. package/dist/primitives/composer/ComposerInput.js +18 -16
  49. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  50. package/dist/primitives/composer/ComposerQuote.js +1 -1
  51. package/dist/primitives/composer/ComposerQuote.js.map +1 -1
  52. package/dist/primitives/composer/ComposerSend.js +1 -0
  53. package/dist/primitives/composer/ComposerSend.js.map +1 -1
  54. package/dist/primitives/composer/ComposerStopDictation.js +4 -3
  55. package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
  56. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  57. package/dist/primitives/composer/trigger/triggerSelectionResource.js +3 -3
  58. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  59. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  60. package/dist/primitives/message/MessagePartsGrouped.js +67 -89
  61. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  62. package/dist/primitives/message/MessageRoot.js +3 -3
  63. package/dist/primitives/message/MessageRoot.js.map +1 -1
  64. package/dist/primitives/queueItem/QueueItemRemove.js +4 -3
  65. package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
  66. package/dist/primitives/queueItem/QueueItemSteer.js +4 -3
  67. package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
  68. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js +1 -1
  69. package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
  70. package/dist/primitives/suggestion/SuggestionTrigger.js +17 -15
  71. package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
  72. package/dist/primitives/thread/ThreadScrollToBottom.js +1 -0
  73. package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
  74. package/dist/unstable/useComposerInput.js +2 -2
  75. package/dist/unstable/useComposerInput.js.map +1 -1
  76. package/dist/unstable/useComposerInputHistory.js +6 -6
  77. package/dist/unstable/useComposerInputHistory.js.map +1 -1
  78. package/dist/unstable/useMentionAdapter.js +1 -1
  79. package/dist/unstable/useMentionAdapter.js.map +1 -1
  80. package/dist/utils/hooks/useOnResizeContent.js +1 -0
  81. package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
  82. package/dist/utils/hooks/useSizeHandle.js +1 -0
  83. package/dist/utils/hooks/useSizeHandle.js.map +1 -1
  84. package/dist/utils/smooth/SmoothContext.js +3 -3
  85. package/dist/utils/smooth/SmoothContext.js.map +1 -1
  86. package/dist/utils/smooth/useSmooth.d.ts.map +1 -1
  87. package/dist/utils/smooth/useSmooth.js +3 -3
  88. package/dist/utils/smooth/useSmooth.js.map +1 -1
  89. package/package.json +9 -9
  90. package/src/client/ExternalThread.ts +345 -56
  91. package/src/client/InMemoryThreadList.ts +3 -3
  92. package/src/context/index.ts +0 -44
  93. package/src/legacy-runtime/cloud/auiV0.ts +14 -0
  94. package/src/legacy-runtime/runtime-cores/assistant-transport/commandQueue.ts +4 -1
  95. package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +37 -10
  96. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-delivery.test.tsx +79 -0
  97. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-parentid.test.tsx +97 -0
  98. package/src/legacy-runtime/runtime-cores/assistant-transport/transport-scheduling.test.ts +113 -2
  99. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransport.spec.md +10 -2
  100. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.test.tsx +185 -0
  101. package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.ts +21 -5
  102. package/src/mcp-apps/McpAppRenderer.tsx +1 -1
  103. package/src/model-context/makeAssistantVisible.test.tsx +1 -1
  104. package/src/model-context/makeAssistantVisible.tsx +1 -1
  105. package/src/primitives/actionBar/ActionBarExportMarkdown.tsx +1 -1
  106. package/src/primitives/actionBar/useActionBarFloatStatus.ts +7 -5
  107. package/src/primitives/attachment/AttachmentRemove.ts +1 -1
  108. package/src/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.ts +1 -1
  109. package/src/primitives/composer/ComposerAddAttachment.test.tsx +108 -0
  110. package/src/primitives/composer/ComposerAddAttachment.ts +11 -5
  111. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +5 -4
  112. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +4 -3
  113. package/src/primitives/composer/ComposerInput.test.tsx +104 -7
  114. package/src/primitives/composer/ComposerInput.tsx +22 -20
  115. package/src/primitives/composer/ComposerQuote.tsx +1 -1
  116. package/src/primitives/composer/ComposerStopDictation.ts +1 -1
  117. package/src/primitives/composer/trigger/triggerSelectionResource.ts +7 -9
  118. package/src/primitives/message/MessagePartsGrouped.tsx +6 -8
  119. package/src/primitives/message/MessageRoot.tsx +1 -1
  120. package/src/primitives/queueItem/QueueItemRemove.ts +1 -1
  121. package/src/primitives/queueItem/QueueItemSteer.ts +1 -1
  122. package/src/primitives/selectionToolbar/SelectionToolbarQuote.tsx +1 -1
  123. package/src/primitives/suggestion/SuggestionTrigger.ts +9 -9
  124. package/src/tests/DataRenderers.test.tsx +212 -0
  125. package/src/tests/RemoteThreadListRuntime.adapterProvider.test.tsx +1 -11
  126. package/src/tests/RemoteThreadListRuntime.deferredProvider.test.tsx +3 -3
  127. package/src/tests/external-thread-attachments.test.tsx +300 -11
  128. package/src/tests/external-thread-branches.test.tsx +10 -10
  129. package/src/tests/external-thread-parity.test.tsx +230 -0
  130. package/src/tests/generative-ui.test.tsx +1 -46
  131. package/src/tests/local-runtime-queue.test.tsx +29 -34
  132. package/src/tests/messagePartSwitchRace.test.tsx +46 -15
  133. package/src/tests/messagePartTypeMismatch.test.tsx +2 -2
  134. package/src/tests/threadMessageById.test.tsx +0 -7
  135. package/src/unstable/useComposerInput.test.tsx +2 -2
  136. package/src/unstable/useComposerInput.ts +2 -2
  137. package/src/unstable/useComposerInputHistory.test.tsx +3 -3
  138. package/src/unstable/useComposerInputHistory.ts +6 -6
  139. package/src/unstable/useMentionAdapter.ts +1 -1
  140. package/src/utils/smooth/SmoothContext.tsx +1 -1
  141. package/src/utils/smooth/useSmooth.test.tsx +1 -1
  142. package/src/utils/smooth/useSmooth.ts +8 -5
  143. package/dist/context/react/utils/createStateHookForRuntime.d.ts +0 -34
  144. package/dist/context/react/utils/createStateHookForRuntime.d.ts.map +0 -1
  145. package/dist/context/react/utils/createStateHookForRuntime.js +0 -21
  146. package/dist/context/react/utils/createStateHookForRuntime.js.map +0 -1
  147. package/dist/legacy-runtime/hooks/AssistantContext.d.ts +0 -74
  148. package/dist/legacy-runtime/hooks/AssistantContext.d.ts.map +0 -1
  149. package/dist/legacy-runtime/hooks/AssistantContext.js +0 -29
  150. package/dist/legacy-runtime/hooks/AssistantContext.js.map +0 -1
  151. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts +0 -1007
  152. package/dist/legacy-runtime/hooks/AttachmentContext.d.ts.map +0 -1
  153. package/dist/legacy-runtime/hooks/AttachmentContext.js +0 -47
  154. package/dist/legacy-runtime/hooks/AttachmentContext.js.map +0 -1
  155. package/dist/legacy-runtime/hooks/ComposerContext.d.ts +0 -141
  156. package/dist/legacy-runtime/hooks/ComposerContext.d.ts.map +0 -1
  157. package/dist/legacy-runtime/hooks/ComposerContext.js +0 -65
  158. package/dist/legacy-runtime/hooks/ComposerContext.js.map +0 -1
  159. package/dist/legacy-runtime/hooks/MessageContext.d.ts +0 -155
  160. package/dist/legacy-runtime/hooks/MessageContext.d.ts.map +0 -1
  161. package/dist/legacy-runtime/hooks/MessageContext.js +0 -68
  162. package/dist/legacy-runtime/hooks/MessageContext.js.map +0 -1
  163. package/dist/legacy-runtime/hooks/MessagePartContext.d.ts +0 -44
  164. package/dist/legacy-runtime/hooks/MessagePartContext.d.ts.map +0 -1
  165. package/dist/legacy-runtime/hooks/MessagePartContext.js +0 -26
  166. package/dist/legacy-runtime/hooks/MessagePartContext.js.map +0 -1
  167. package/dist/legacy-runtime/hooks/ThreadContext.d.ts +0 -139
  168. package/dist/legacy-runtime/hooks/ThreadContext.d.ts.map +0 -1
  169. package/dist/legacy-runtime/hooks/ThreadContext.js +0 -84
  170. package/dist/legacy-runtime/hooks/ThreadContext.js.map +0 -1
  171. package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts +0 -44
  172. package/dist/legacy-runtime/hooks/ThreadListItemContext.d.ts.map +0 -1
  173. package/dist/legacy-runtime/hooks/ThreadListItemContext.js +0 -26
  174. package/dist/legacy-runtime/hooks/ThreadListItemContext.js.map +0 -1
  175. package/src/context/react/utils/createStateHookForRuntime.ts +0 -82
  176. package/src/legacy-runtime/hooks/AssistantContext.ts +0 -67
  177. package/src/legacy-runtime/hooks/AttachmentContext.ts +0 -99
  178. package/src/legacy-runtime/hooks/ComposerContext.ts +0 -131
  179. package/src/legacy-runtime/hooks/MessageContext.ts +0 -127
  180. package/src/legacy-runtime/hooks/MessagePartContext.ts +0 -32
  181. package/src/legacy-runtime/hooks/ThreadContext.ts +0 -120
  182. package/src/legacy-runtime/hooks/ThreadListItemContext.ts +0 -36
  183. package/src/tests/BaseComposerRuntimeCore.test.ts +0 -621
  184. package/src/tests/BaseSubscribable.test.ts +0 -54
  185. package/src/tests/DataRenderers.test.ts +0 -355
  186. package/src/tests/ExternalStoreThreadRuntimeCore.test.ts +0 -685
  187. package/src/tests/LocalThreadRuntimeCore.test.ts +0 -324
  188. package/src/tests/auiV0Encode.test.ts +0 -188
  189. package/src/tests/external-message-converter.test.ts +0 -346
  190. package/src/tests/shouldContinue.test.ts +0 -122
@@ -29,7 +29,7 @@ function unstable_useMentionAdapter(options) {
29
29
  adapter: useMemo(() => {
30
30
  const getModelContextTools = () => {
31
31
  if (!wantsTools) return [];
32
- const tools = aui.thread().getModelContext().tools;
32
+ const tools = aui.thread.getModelContext().tools;
33
33
  if (!tools) return [];
34
34
  const formatLabel = toolsConfig?.formatLabel;
35
35
  const defaultIcon = toolsConfig?.icon;
@@ -1 +1 @@
1
- {"version":3,"file":"useMentionAdapter.js","names":["useMemo","FC","useAui","Unstable_DirectiveFormatter","Unstable_TriggerAdapter","Unstable_TriggerCategory","Unstable_TriggerItem","unstable_defaultDirectiveFormatter","ReadonlyJSONObject","Unstable_IconComponent","className","Unstable_Mention","id","type","label","description","icon","metadata","Unstable_MentionCategory","items","Unstable_ModelContextToolsOptions","category","formatLabel","toolName","Unstable_UseMentionAdapterOptions","categories","includeModelContextTools","formatter","onInserted","item","iconMap","Record","fallbackIcon","Unstable_MentionDirective","unstable_useMentionAdapter","options","adapter","directive","aui","includeTools","toolsConfig","undefined","wantsTools","getModelContextTools","ctx","thread","getModelContext","tools","defaultIcon","Object","entries","map","name","tool","toTriggerItem","length","groups","cat","toolCategory","toolItems","allGroups","categoryItems","find","g","search","query","lower","toLowerCase","flatMap","filter","matchesQuery","flatItems","getFlatPool","seen","Set","i","t","has","m","includes"],"sources":["../../src/unstable/useMentionAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type FC } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type {\n Unstable_DirectiveFormatter,\n Unstable_TriggerAdapter,\n Unstable_TriggerCategory,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport { unstable_defaultDirectiveFormatter } from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\n\n/** Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`. */\nexport type Unstable_IconComponent = FC<{ className?: string }>;\n\nexport type Unstable_Mention = {\n readonly id: string;\n readonly type: string;\n readonly label: string;\n readonly description?: string | undefined;\n /** Shortcut for `metadata.icon`; merged with `metadata` if both are given. */\n readonly icon?: string | undefined;\n readonly metadata?: ReadonlyJSONObject | undefined;\n};\n\nexport type Unstable_MentionCategory = {\n readonly id: string;\n readonly label: string;\n readonly items: readonly Unstable_Mention[];\n};\n\nexport type Unstable_ModelContextToolsOptions = {\n /** Wrap tools in a dedicated category (drill-down mode). */\n readonly category?: { readonly id: string; readonly label: string };\n /** Format tool name for display. */\n readonly formatLabel?: (toolName: string) => string;\n /** Default icon key for each tool. */\n readonly icon?: string;\n};\n\nexport type Unstable_UseMentionAdapterOptions = {\n /** Flat mention list. Ignored when `categories` is set. */\n readonly items?: readonly Unstable_Mention[];\n /** Categorized mentions for drill-down navigation. */\n readonly categories?: readonly Unstable_MentionCategory[];\n /**\n * How tools registered in model context integrate.\n * - `false`: exclude.\n * - `true`: include (default when no `items`/`categories`; as a category\n * if `categories` is set, flat otherwise).\n * - object: explicit config.\n *\n * Omitted → defaults to `true` iff neither `items` nor `categories`.\n */\n readonly includeModelContextTools?:\n | boolean\n | Unstable_ModelContextToolsOptions;\n /** Directive formatter. @default unstable_defaultDirectiveFormatter */\n readonly formatter?: Unstable_DirectiveFormatter;\n /** Fires after an item is inserted into the composer. */\n readonly onInserted?: (item: Unstable_TriggerItem) => void;\n /** Maps `metadata.icon` / `category.id` string keys to React components. */\n readonly iconMap?: Record<string, Unstable_IconComponent>;\n /** Fallback icon when no entry in `iconMap` matches. */\n readonly fallbackIcon?: Unstable_IconComponent;\n};\n\nexport type Unstable_MentionDirective = {\n readonly formatter: Unstable_DirectiveFormatter;\n readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Creates a spreadable `{ adapter, directive }` bundle for `@` mentions.\n * Supports tools registered in model context, explicit items, or both —\n * flat or categorized.\n *\n * @example\n * ```tsx\n * const mention = unstable_useMentionAdapter();\n * <ComposerTriggerPopover char=\"@\" {...mention} />\n * ```\n */\nexport function unstable_useMentionAdapter(\n options?: Unstable_UseMentionAdapterOptions,\n): {\n adapter: Unstable_TriggerAdapter;\n directive: Unstable_MentionDirective;\n iconMap?: Record<string, Unstable_IconComponent>;\n fallbackIcon?: Unstable_IconComponent;\n} {\n const aui = useAui();\n\n const items = options?.items;\n const categories = options?.categories;\n const includeTools =\n options?.includeModelContextTools ?? (!items && !categories);\n const toolsConfig =\n typeof includeTools === \"object\" ? includeTools : undefined;\n const wantsTools = includeTools !== false;\n const formatter = options?.formatter;\n const onInserted = options?.onInserted;\n\n const adapter = useMemo<Unstable_TriggerAdapter>(() => {\n const getModelContextTools = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return [];\n const ctx = aui.thread().getModelContext();\n const tools = ctx.tools;\n if (!tools) return [];\n const formatLabel = toolsConfig?.formatLabel;\n const defaultIcon = toolsConfig?.icon;\n return Object.entries(tools).map(([name, tool]) =>\n toTriggerItem({\n id: name,\n type: \"tool\",\n label: formatLabel ? formatLabel(name) : name,\n description: tool.description ?? undefined,\n icon: defaultIcon,\n }),\n );\n };\n\n // Categorized: drill-down mode\n if (categories && categories.length > 0) {\n const groups = categories.map((cat) => ({\n id: cat.id,\n label: cat.label,\n items: cat.items.map(toTriggerItem),\n }));\n\n let toolCategory: {\n id: string;\n label: string;\n items: Unstable_TriggerItem[];\n } | null = null;\n if (wantsTools) {\n const toolItems = getModelContextTools();\n if (toolItems.length > 0) {\n toolCategory = {\n id: toolsConfig?.category?.id ?? \"tools\",\n label: toolsConfig?.category?.label ?? \"Tools\",\n items: toolItems,\n };\n }\n }\n const allGroups = toolCategory ? [...groups, toolCategory] : groups;\n\n return {\n categories: () => allGroups.map(({ id, label }) => ({ id, label })),\n categoryItems: (id) => allGroups.find((g) => g.id === id)?.items ?? [],\n search: (query) => {\n const lower = query.toLowerCase();\n return allGroups\n .flatMap((g) => g.items)\n .filter((item) => matchesQuery(item, lower));\n },\n };\n }\n\n // Flat: items + (optionally) tools, all in one search pool\n const flatItems = (items ?? []).map(toTriggerItem);\n const getFlatPool = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return flatItems;\n const toolItems = getModelContextTools();\n // Dedupe by id — explicit items win.\n const seen = new Set(flatItems.map((i) => i.id));\n return [...flatItems, ...toolItems.filter((t) => !seen.has(t.id))];\n };\n\n return {\n categories: (): readonly Unstable_TriggerCategory[] => [],\n categoryItems: () => [],\n search: (query) => {\n const lower = query.toLowerCase();\n return getFlatPool().filter((item) => matchesQuery(item, lower));\n },\n };\n }, [aui, items, categories, wantsTools, toolsConfig]);\n\n const directive = useMemo<Unstable_MentionDirective>(\n () => ({\n formatter: formatter ?? unstable_defaultDirectiveFormatter,\n ...(onInserted ? { onInserted } : {}),\n }),\n [formatter, onInserted],\n );\n\n return {\n adapter,\n directive,\n ...(options?.iconMap ? { iconMap: options.iconMap } : {}),\n ...(options?.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),\n };\n}\n\nfunction toTriggerItem(m: Unstable_Mention): Unstable_TriggerItem {\n const metadata =\n m.icon !== undefined ? { ...(m.metadata ?? {}), icon: m.icon } : m.metadata;\n return {\n id: m.id,\n type: m.type,\n label: m.label,\n ...(m.description !== undefined ? { description: m.description } : {}),\n ...(metadata !== undefined ? { metadata } : {}),\n };\n}\n\nfunction matchesQuery(item: Unstable_TriggerItem, lower: string): boolean {\n if (!lower) return true;\n if (item.id.toLowerCase().includes(lower)) return true;\n if (item.label.toLowerCase().includes(lower)) return true;\n if (item.description?.toLowerCase().includes(lower)) return true;\n return false;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useMentionAdapter.js","names":["useMemo","FC","useAui","Unstable_DirectiveFormatter","Unstable_TriggerAdapter","Unstable_TriggerCategory","Unstable_TriggerItem","unstable_defaultDirectiveFormatter","ReadonlyJSONObject","Unstable_IconComponent","className","Unstable_Mention","id","type","label","description","icon","metadata","Unstable_MentionCategory","items","Unstable_ModelContextToolsOptions","category","formatLabel","toolName","Unstable_UseMentionAdapterOptions","categories","includeModelContextTools","formatter","onInserted","item","iconMap","Record","fallbackIcon","Unstable_MentionDirective","unstable_useMentionAdapter","options","adapter","directive","aui","includeTools","toolsConfig","undefined","wantsTools","getModelContextTools","ctx","thread","getModelContext","tools","defaultIcon","Object","entries","map","name","tool","toTriggerItem","length","groups","cat","toolCategory","toolItems","allGroups","categoryItems","find","g","search","query","lower","toLowerCase","flatMap","filter","matchesQuery","flatItems","getFlatPool","seen","Set","i","t","has","m","includes"],"sources":["../../src/unstable/useMentionAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useMemo, type FC } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport type {\n Unstable_DirectiveFormatter,\n Unstable_TriggerAdapter,\n Unstable_TriggerCategory,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\nimport { unstable_defaultDirectiveFormatter } from \"@assistant-ui/core\";\nimport type { ReadonlyJSONObject } from \"assistant-stream/utils\";\n\n/** Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`. */\nexport type Unstable_IconComponent = FC<{ className?: string }>;\n\nexport type Unstable_Mention = {\n readonly id: string;\n readonly type: string;\n readonly label: string;\n readonly description?: string | undefined;\n /** Shortcut for `metadata.icon`; merged with `metadata` if both are given. */\n readonly icon?: string | undefined;\n readonly metadata?: ReadonlyJSONObject | undefined;\n};\n\nexport type Unstable_MentionCategory = {\n readonly id: string;\n readonly label: string;\n readonly items: readonly Unstable_Mention[];\n};\n\nexport type Unstable_ModelContextToolsOptions = {\n /** Wrap tools in a dedicated category (drill-down mode). */\n readonly category?: { readonly id: string; readonly label: string };\n /** Format tool name for display. */\n readonly formatLabel?: (toolName: string) => string;\n /** Default icon key for each tool. */\n readonly icon?: string;\n};\n\nexport type Unstable_UseMentionAdapterOptions = {\n /** Flat mention list. Ignored when `categories` is set. */\n readonly items?: readonly Unstable_Mention[];\n /** Categorized mentions for drill-down navigation. */\n readonly categories?: readonly Unstable_MentionCategory[];\n /**\n * How tools registered in model context integrate.\n * - `false`: exclude.\n * - `true`: include (default when no `items`/`categories`; as a category\n * if `categories` is set, flat otherwise).\n * - object: explicit config.\n *\n * Omitted → defaults to `true` iff neither `items` nor `categories`.\n */\n readonly includeModelContextTools?:\n | boolean\n | Unstable_ModelContextToolsOptions;\n /** Directive formatter. @default unstable_defaultDirectiveFormatter */\n readonly formatter?: Unstable_DirectiveFormatter;\n /** Fires after an item is inserted into the composer. */\n readonly onInserted?: (item: Unstable_TriggerItem) => void;\n /** Maps `metadata.icon` / `category.id` string keys to React components. */\n readonly iconMap?: Record<string, Unstable_IconComponent>;\n /** Fallback icon when no entry in `iconMap` matches. */\n readonly fallbackIcon?: Unstable_IconComponent;\n};\n\nexport type Unstable_MentionDirective = {\n readonly formatter: Unstable_DirectiveFormatter;\n readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Creates a spreadable `{ adapter, directive }` bundle for `@` mentions.\n * Supports tools registered in model context, explicit items, or both —\n * flat or categorized.\n *\n * @example\n * ```tsx\n * const mention = unstable_useMentionAdapter();\n * <ComposerTriggerPopover char=\"@\" {...mention} />\n * ```\n */\nexport function unstable_useMentionAdapter(\n options?: Unstable_UseMentionAdapterOptions,\n): {\n adapter: Unstable_TriggerAdapter;\n directive: Unstable_MentionDirective;\n iconMap?: Record<string, Unstable_IconComponent>;\n fallbackIcon?: Unstable_IconComponent;\n} {\n const aui = useAui();\n\n const items = options?.items;\n const categories = options?.categories;\n const includeTools =\n options?.includeModelContextTools ?? (!items && !categories);\n const toolsConfig =\n typeof includeTools === \"object\" ? includeTools : undefined;\n const wantsTools = includeTools !== false;\n const formatter = options?.formatter;\n const onInserted = options?.onInserted;\n\n const adapter = useMemo<Unstable_TriggerAdapter>(() => {\n const getModelContextTools = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return [];\n const ctx = aui.thread.getModelContext();\n const tools = ctx.tools;\n if (!tools) return [];\n const formatLabel = toolsConfig?.formatLabel;\n const defaultIcon = toolsConfig?.icon;\n return Object.entries(tools).map(([name, tool]) =>\n toTriggerItem({\n id: name,\n type: \"tool\",\n label: formatLabel ? formatLabel(name) : name,\n description: tool.description ?? undefined,\n icon: defaultIcon,\n }),\n );\n };\n\n // Categorized: drill-down mode\n if (categories && categories.length > 0) {\n const groups = categories.map((cat) => ({\n id: cat.id,\n label: cat.label,\n items: cat.items.map(toTriggerItem),\n }));\n\n let toolCategory: {\n id: string;\n label: string;\n items: Unstable_TriggerItem[];\n } | null = null;\n if (wantsTools) {\n const toolItems = getModelContextTools();\n if (toolItems.length > 0) {\n toolCategory = {\n id: toolsConfig?.category?.id ?? \"tools\",\n label: toolsConfig?.category?.label ?? \"Tools\",\n items: toolItems,\n };\n }\n }\n const allGroups = toolCategory ? [...groups, toolCategory] : groups;\n\n return {\n categories: () => allGroups.map(({ id, label }) => ({ id, label })),\n categoryItems: (id) => allGroups.find((g) => g.id === id)?.items ?? [],\n search: (query) => {\n const lower = query.toLowerCase();\n return allGroups\n .flatMap((g) => g.items)\n .filter((item) => matchesQuery(item, lower));\n },\n };\n }\n\n // Flat: items + (optionally) tools, all in one search pool\n const flatItems = (items ?? []).map(toTriggerItem);\n const getFlatPool = (): Unstable_TriggerItem[] => {\n if (!wantsTools) return flatItems;\n const toolItems = getModelContextTools();\n // Dedupe by id — explicit items win.\n const seen = new Set(flatItems.map((i) => i.id));\n return [...flatItems, ...toolItems.filter((t) => !seen.has(t.id))];\n };\n\n return {\n categories: (): readonly Unstable_TriggerCategory[] => [],\n categoryItems: () => [],\n search: (query) => {\n const lower = query.toLowerCase();\n return getFlatPool().filter((item) => matchesQuery(item, lower));\n },\n };\n }, [aui, items, categories, wantsTools, toolsConfig]);\n\n const directive = useMemo<Unstable_MentionDirective>(\n () => ({\n formatter: formatter ?? unstable_defaultDirectiveFormatter,\n ...(onInserted ? { onInserted } : {}),\n }),\n [formatter, onInserted],\n );\n\n return {\n adapter,\n directive,\n ...(options?.iconMap ? { iconMap: options.iconMap } : {}),\n ...(options?.fallbackIcon ? { fallbackIcon: options.fallbackIcon } : {}),\n };\n}\n\nfunction toTriggerItem(m: Unstable_Mention): Unstable_TriggerItem {\n const metadata =\n m.icon !== undefined ? { ...(m.metadata ?? {}), icon: m.icon } : m.metadata;\n return {\n id: m.id,\n type: m.type,\n label: m.label,\n ...(m.description !== undefined ? { description: m.description } : {}),\n ...(metadata !== undefined ? { metadata } : {}),\n };\n}\n\nfunction matchesQuery(item: Unstable_TriggerItem, lower: string): boolean {\n if (!lower) return true;\n if (item.id.toLowerCase().includes(lower)) return true;\n if (item.label.toLowerCase().includes(lower)) return true;\n if (item.description?.toLowerCase().includes(lower)) return true;\n return false;\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { useManagedRef } from "./useManagedRef.js";
2
2
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
+ import "@assistant-ui/tap/react-shim";
3
4
  import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
4
5
  //#region src/utils/hooks/useOnResizeContent.ts
5
6
  const useOnResizeContent = (callback) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","resizeObserver","ResizeObserver","mutationObserver","MutationObserver","mutations","hasRelevantMutation","some","_temp","observe","childList","subtree","attributes","characterData","disconnect","refCallback","m","type","attributeName"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => {\n const resizeObserver = new ResizeObserver(() => {\n callbackRef();\n });\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // with code paths that write styles in response to viewport changes.\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n callbackRef();\n }\n });\n\n resizeObserver.observe(el);\n mutationObserver.observe(el, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n },\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;AAIA,MAAaG,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAA;GACE,MAAAC,iBAAuB,IAAIC,qBAAe;IACxCJ,YAAY;GAAC,CACd;GAED,MAAAK,mBAAyB,IAAIC,kBAAiBC,cAAA;IAM5C,IAH4BA,UAASE,KACnCC,KAEEF,GACFR,YAAY;GACb,CACF;GAEDG,eAAcQ,QAAST,EAAE;GACzBG,iBAAgBM,QAAST,IAAI;IAAAU,WAChB;IAAIC,SACN;IAAIC,YACD;IAAIC,eACD;GACjB,CAAC;GAAC,aAEK;IACLZ,eAAca,WAAY;IAC1BX,iBAAgBW,WAAY;GAAC;EAC9B;EACFlB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAED,OAEKH,cAAcsB,EAAW;AAAC;AApCD,SAAAP,MAAAQ,GAAA;CAAA,OAajBA,EAACC,SAAU,gBAAgBD,EAACE,kBAAmB;AAAO"}
1
+ {"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","resizeObserver","ResizeObserver","mutationObserver","MutationObserver","mutations","hasRelevantMutation","some","_temp","observe","childList","subtree","attributes","characterData","disconnect","refCallback","m","type","attributeName"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => {\n const resizeObserver = new ResizeObserver(() => {\n callbackRef();\n });\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // with code paths that write styles in response to viewport changes.\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n callbackRef();\n }\n });\n\n resizeObserver.observe(el);\n mutationObserver.observe(el, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n },\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;;AAIA,MAAaG,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAA;GACE,MAAAC,iBAAuB,IAAIC,qBAAe;IACxCJ,YAAY;GAAC,CACd;GAED,MAAAK,mBAAyB,IAAIC,kBAAiBC,cAAA;IAM5C,IAH4BA,UAASE,KACnCC,KAEEF,GACFR,YAAY;GACb,CACF;GAEDG,eAAcQ,QAAST,EAAE;GACzBG,iBAAgBM,QAAST,IAAI;IAAAU,WAChB;IAAIC,SACN;IAAIC,YACD;IAAIC,eACD;GACjB,CAAC;GAAC,aAEK;IACLZ,eAAca,WAAY;IAC1BX,iBAAgBW,WAAY;GAAC;EAC9B;EACFlB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAED,OAEKH,cAAcsB,EAAW;AAAC;AApCD,SAAAP,MAAAQ,GAAA;CAAA,OAajBA,EAACC,SAAU,gBAAgBD,EAACE,kBAAmB;AAAO"}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { useManagedRef } from "./useManagedRef.js";
3
3
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
4
+ import "@assistant-ui/tap/react-shim";
4
5
  //#region src/utils/hooks/useSizeHandle.ts
5
6
  /**
6
7
  * Hook that creates a ref for tracking element size via a SizeHandle.
@@ -1 +1 @@
1
- {"version":3,"file":"useSizeHandle.js","names":["c","_c","useCallback","useManagedRef","SizeHandle","useSizeHandle","register","getHeight","$","t0","el","sizeHandle","updateHeight","height","offsetHeight","setHeight","ro","ResizeObserver","observe","disconnect","unregister","callbackRef"],"sources":["../../../src/utils/hooks/useSizeHandle.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\nimport type { SizeHandle } from \"../../context/stores/ThreadViewport\";\n\n/**\n * Hook that creates a ref for tracking element size via a SizeHandle.\n * Automatically sets up ResizeObserver and reports height changes.\n *\n * @param register - Function that returns a SizeHandle (e.g., registerContentInset)\n * @param getHeight - Optional function to compute height (defaults to el.offsetHeight)\n * @returns A ref callback to attach to the element\n */\nexport const useSizeHandle = (\n register: (() => SizeHandle) | null | undefined,\n getHeight?: (el: HTMLElement) => number,\n) => {\n const callbackRef = useCallback(\n (el: HTMLElement) => {\n if (!register) return;\n\n const sizeHandle = register();\n\n const updateHeight = () => {\n const height = getHeight ? getHeight(el) : el.offsetHeight;\n sizeHandle.setHeight(height);\n };\n\n const ro = new ResizeObserver(updateHeight);\n ro.observe(el);\n updateHeight();\n\n return () => {\n ro.disconnect();\n sizeHandle.unregister();\n };\n },\n [register, getHeight],\n );\n\n return useManagedRef(callbackRef);\n};\n"],"mappings":";;;;;;;;;;;;AAcA,MAAaK,iBAAgBC,UAAAC,cAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,aAAAC,EAAA,OAAAF,UAAA;EAKzBG,MAAAC,OAAA;GACE,IAAI,CAACJ,UAAQ;GAEb,MAAAK,aAAmBL,SAAS;GAE5B,MAAAM,qBAAqB;IACnB,MAAAC,SAAeN,YAAYA,UAAUG,EAAoB,IAAdA,GAAEI;IAC7CH,WAAUI,UAAWF,MAAM;GAAC;GAG9B,MAAAG,KAAW,IAAIC,eAAeL,YAAY;GAC1CI,GAAEE,QAASR,EAAE;GACbE,aAAa;GAAC,aAEP;IACLI,GAAEG,WAAY;IACdR,WAAUS,WAAY;GAAC;EACxB;EACFZ,EAAA,KAAAD;EAAAC,EAAA,KAAAF;EAAAE,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAED,OAEKL,cAAckB,EAAW;AAAC"}
1
+ {"version":3,"file":"useSizeHandle.js","names":["c","_c","useCallback","useManagedRef","SizeHandle","useSizeHandle","register","getHeight","$","t0","el","sizeHandle","updateHeight","height","offsetHeight","setHeight","ro","ResizeObserver","observe","disconnect","unregister","callbackRef"],"sources":["../../../src/utils/hooks/useSizeHandle.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\nimport type { SizeHandle } from \"../../context/stores/ThreadViewport\";\n\n/**\n * Hook that creates a ref for tracking element size via a SizeHandle.\n * Automatically sets up ResizeObserver and reports height changes.\n *\n * @param register - Function that returns a SizeHandle (e.g., registerContentInset)\n * @param getHeight - Optional function to compute height (defaults to el.offsetHeight)\n * @returns A ref callback to attach to the element\n */\nexport const useSizeHandle = (\n register: (() => SizeHandle) | null | undefined,\n getHeight?: (el: HTMLElement) => number,\n) => {\n const callbackRef = useCallback(\n (el: HTMLElement) => {\n if (!register) return;\n\n const sizeHandle = register();\n\n const updateHeight = () => {\n const height = getHeight ? getHeight(el) : el.offsetHeight;\n sizeHandle.setHeight(height);\n };\n\n const ro = new ResizeObserver(updateHeight);\n ro.observe(el);\n updateHeight();\n\n return () => {\n ro.disconnect();\n sizeHandle.unregister();\n };\n },\n [register, getHeight],\n );\n\n return useManagedRef(callbackRef);\n};\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAaK,iBAAgBC,UAAAC,cAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,aAAAC,EAAA,OAAAF,UAAA;EAKzBG,MAAAC,OAAA;GACE,IAAI,CAACJ,UAAQ;GAEb,MAAAK,aAAmBL,SAAS;GAE5B,MAAAM,qBAAqB;IACnB,MAAAC,SAAeN,YAAYA,UAAUG,EAAoB,IAAdA,GAAEI;IAC7CH,WAAUI,UAAWF,MAAM;GAAC;GAG9B,MAAAG,KAAW,IAAIC,eAAeL,YAAY;GAC1CI,GAAEE,QAASR,EAAE;GACbE,aAAa;GAAC,aAEP;IACLI,GAAEG,WAAY;IACdR,WAAUS,WAAY;GAAC;EACxB;EACFZ,EAAA,KAAAD;EAAAC,EAAA,KAAAF;EAAAE,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAED,OAEKL,cAAckB,EAAW;AAAC"}
@@ -21,9 +21,9 @@ const SmoothContextProvider = (t0) => {
21
21
  const outer = useSmoothContext(t1);
22
22
  const aui = useAui();
23
23
  let t2;
24
- if ($[1] !== aui) {
25
- t2 = () => makeSmoothContext(aui.part().getState().status);
26
- $[1] = aui;
24
+ if ($[1] !== aui.part) {
25
+ t2 = () => makeSmoothContext(aui.part.getState().status);
26
+ $[1] = aui.part;
27
27
  $[2] = t2;
28
28
  } else t2 = $[2];
29
29
  const [context] = useState(t2);
@@ -1 +1 @@
1
- {"version":3,"file":"SmoothContext.js","names":["c","_c","ComponentType","createContext","FC","forwardRef","PropsWithChildren","useContext","useState","ReadonlyStore","create","UseBoundStore","MessagePartStatus","ToolCallMessagePartStatus","useAui","createContextStoreHook","SmoothContextValue","useSmoothStatus","SmoothContext","makeSmoothContext","initialState","SmoothContextProvider","t0","$","children","t1","Symbol","for","optional","outer","useSmoothContext","aui","t2","part","getState","status","context","t3","withSmoothContextProvider","Component","C","Wrapped","props","ref","displayName","options","Error","useSmoothStatusStore"],"sources":["../../../src/utils/smooth/SmoothContext.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n createContext,\n type FC,\n forwardRef,\n type PropsWithChildren,\n useContext,\n useState,\n} from \"react\";\nimport type { ReadonlyStore } from \"../../context/ReadonlyStore\";\nimport { create, type UseBoundStore } from \"zustand\";\nimport type {\n MessagePartStatus,\n ToolCallMessagePartStatus,\n} from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { createContextStoreHook } from \"../../context/react/utils/createContextStoreHook\";\n\ntype SmoothContextValue = {\n useSmoothStatus: UseBoundStore<\n ReadonlyStore<MessagePartStatus | ToolCallMessagePartStatus>\n >;\n};\n\nconst SmoothContext = createContext<SmoothContextValue | null>(null);\n\nconst makeSmoothContext = (\n initialState: MessagePartStatus | ToolCallMessagePartStatus,\n) => {\n const useSmoothStatus = create(() => initialState);\n return { useSmoothStatus };\n};\n\nexport const SmoothContextProvider: FC<PropsWithChildren> = ({ children }) => {\n const outer = useSmoothContext({ optional: true });\n const aui = useAui();\n\n const [context] = useState(() =>\n makeSmoothContext(aui.part().getState().status),\n );\n\n // do not wrap if there is an outer SmoothContextProvider\n if (outer) return children;\n\n return (\n <SmoothContext.Provider value={context}>{children}</SmoothContext.Provider>\n );\n};\n\nexport const withSmoothContextProvider = <C extends ComponentType<any>>(\n Component: C,\n): C => {\n const Wrapped = forwardRef((props, ref) => {\n return (\n <SmoothContextProvider>\n <Component {...(props as any)} ref={ref} />\n </SmoothContextProvider>\n );\n });\n Wrapped.displayName = Component.displayName;\n return Wrapped as any;\n};\n\nfunction useSmoothContext(options?: {\n optional?: false | undefined;\n}): SmoothContextValue;\nfunction useSmoothContext(options?: {\n optional?: boolean | undefined;\n}): SmoothContextValue | null;\nfunction useSmoothContext(options?: { optional?: boolean | undefined }) {\n const context = useContext(SmoothContext);\n if (!options?.optional && !context)\n throw new Error(\n \"This component must be used within a SmoothContextProvider.\",\n );\n return context;\n}\n\nexport const { useSmoothStatus, useSmoothStatusStore } = createContextStoreHook(\n useSmoothContext,\n \"useSmoothStatus\",\n);\n"],"mappings":";;;;;;;;AA0BA,MAAMkB,gBAAgBf,cAAyC,IAAI;AAEnE,MAAMgB,qBACJC,iBACG;CAEH,OAAO,EAAEH,iBADeP,aAAaU,YAC5BH,EAAgB;AAC3B;AAEA,MAAaI,yBAA+CC,OAAA;CAAA,MAAAC,IAAAtB,EAAA,CAAA;CAAC,MAAA,EAAAuB,aAAAF;CAAY,IAAAG;CAAA,IAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EACxCF,KAAA,EAAAG,UAAY,KAAK;EAACL,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAjD,MAAAM,QAAcC,iBAAiBL,EAAkB;CACjD,MAAAM,MAAYjB,OAAO;CAAE,IAAAkB;CAAA,IAAAT,EAAA,OAAAQ,KAAA;EAEMC,WACzBb,kBAAkBY,IAAGE,KAAM,CAAC,CAAAC,SAAU,CAAC,CAAAC,MAAO;EAACZ,EAAA,KAAAQ;EAAAR,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CADjD,MAAA,CAAAa,WAAkB5B,SAASwB,EAE3B;CAGA,IAAIH,OAAK,OAASL;CAAS,IAAAa;CAAA,IAAAd,EAAA,OAAAC,YAAAD,EAAA,OAAAa,SAAA;EAGzBC,KAAA,oBAAA,cAAA,UAAA;GAA+BD,OAAAA;GAAUZ;EAAS,CAAA;EAAyBD,EAAA,KAAAC;EAAAD,EAAA,KAAAa;EAAAb,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAAA,OAA3Ec;AAA2E;AAI/E,MAAaC,6BACXC,cACM;CACN,MAAME,UAAUpC,YAAWqC,OAAAC,QAAA;EAAA,MAAApB,IAAAtB,EAAA,CAAA;EAGL,MAAAqB,KAAAoB;EAAY,IAAAjB;EAAA,IAAAF,EAAA,OAAAoB,OAAApB,EAAA,OAAAD,IAAA;GAD9BG,KAAA,oBAAC,uBAAD,EAAA,UACE,oBAAC,WAAD;IAAU,GAAMH;IAAoBqB;GAAG,CAAA,EADnB,CAAA;GAEEpB,EAAA,KAAAoB;GAAApB,EAAA,KAAAD;GAAAC,EAAA,KAAAE;EAAA,OAAAA,KAAAF,EAAA;EAAA,OAFxBE;CAEwB,CAE3B;CACDgB,QAAQG,cAAcL,UAAUK;CAChC,OAAOH;AACT;AAQA,SAAAX,iBAAAe,SAAA;CACE,MAAAT,UAAgB7B,WAAWW,aAAa;CACxC,IAAI,CAAC2B,SAAOjB,YAAR,CAAuBQ,SACzB,MAAM,IAAIU,MACR,6DACF;CAAE,OACGV;AAAO;AAGhB,MAAa,EAAEnB,iBAAiB8B,yBAAyBhC,uBACvDe,kBACA,iBACF"}
1
+ {"version":3,"file":"SmoothContext.js","names":["c","_c","ComponentType","createContext","FC","forwardRef","PropsWithChildren","useContext","useState","ReadonlyStore","create","UseBoundStore","MessagePartStatus","ToolCallMessagePartStatus","useAui","createContextStoreHook","SmoothContextValue","useSmoothStatus","SmoothContext","makeSmoothContext","initialState","SmoothContextProvider","t0","$","children","t1","Symbol","for","optional","outer","useSmoothContext","aui","t2","part","getState","status","context","t3","withSmoothContextProvider","Component","C","Wrapped","props","ref","displayName","options","Error","useSmoothStatusStore"],"sources":["../../../src/utils/smooth/SmoothContext.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n createContext,\n type FC,\n forwardRef,\n type PropsWithChildren,\n useContext,\n useState,\n} from \"react\";\nimport type { ReadonlyStore } from \"../../context/ReadonlyStore\";\nimport { create, type UseBoundStore } from \"zustand\";\nimport type {\n MessagePartStatus,\n ToolCallMessagePartStatus,\n} from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { createContextStoreHook } from \"../../context/react/utils/createContextStoreHook\";\n\ntype SmoothContextValue = {\n useSmoothStatus: UseBoundStore<\n ReadonlyStore<MessagePartStatus | ToolCallMessagePartStatus>\n >;\n};\n\nconst SmoothContext = createContext<SmoothContextValue | null>(null);\n\nconst makeSmoothContext = (\n initialState: MessagePartStatus | ToolCallMessagePartStatus,\n) => {\n const useSmoothStatus = create(() => initialState);\n return { useSmoothStatus };\n};\n\nexport const SmoothContextProvider: FC<PropsWithChildren> = ({ children }) => {\n const outer = useSmoothContext({ optional: true });\n const aui = useAui();\n\n const [context] = useState(() =>\n makeSmoothContext(aui.part.getState().status),\n );\n\n // do not wrap if there is an outer SmoothContextProvider\n if (outer) return children;\n\n return (\n <SmoothContext.Provider value={context}>{children}</SmoothContext.Provider>\n );\n};\n\nexport const withSmoothContextProvider = <C extends ComponentType<any>>(\n Component: C,\n): C => {\n const Wrapped = forwardRef((props, ref) => {\n return (\n <SmoothContextProvider>\n <Component {...(props as any)} ref={ref} />\n </SmoothContextProvider>\n );\n });\n Wrapped.displayName = Component.displayName;\n return Wrapped as any;\n};\n\nfunction useSmoothContext(options?: {\n optional?: false | undefined;\n}): SmoothContextValue;\nfunction useSmoothContext(options?: {\n optional?: boolean | undefined;\n}): SmoothContextValue | null;\nfunction useSmoothContext(options?: { optional?: boolean | undefined }) {\n const context = useContext(SmoothContext);\n if (!options?.optional && !context)\n throw new Error(\n \"This component must be used within a SmoothContextProvider.\",\n );\n return context;\n}\n\nexport const { useSmoothStatus, useSmoothStatusStore } = createContextStoreHook(\n useSmoothContext,\n \"useSmoothStatus\",\n);\n"],"mappings":";;;;;;;;AA0BA,MAAMkB,gBAAgBf,cAAyC,IAAI;AAEnE,MAAMgB,qBACJC,iBACG;CAEH,OAAO,EAAEH,iBADeP,aAAaU,YAC5BH,EAAgB;AAC3B;AAEA,MAAaI,yBAA+CC,OAAA;CAAA,MAAAC,IAAAtB,EAAA,CAAA;CAAC,MAAA,EAAAuB,aAAAF;CAAY,IAAAG;CAAA,IAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EACxCF,KAAA,EAAAG,UAAY,KAAK;EAACL,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAjD,MAAAM,QAAcC,iBAAiBL,EAAkB;CACjD,MAAAM,MAAYjB,OAAO;CAAE,IAAAkB;CAAA,IAAAT,EAAA,OAAAQ,IAAAE,MAAA;EAEMD,WACzBb,kBAAkBY,IAAGE,KAAKC,SAAU,CAAC,CAAAC,MAAO;EAACZ,EAAA,KAAAQ,IAAAE;EAAAV,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAD/C,MAAA,CAAAa,WAAkB5B,SAASwB,EAE3B;CAGA,IAAIH,OAAK,OAASL;CAAS,IAAAa;CAAA,IAAAd,EAAA,OAAAC,YAAAD,EAAA,OAAAa,SAAA;EAGzBC,KAAA,oBAAA,cAAA,UAAA;GAA+BD,OAAAA;GAAUZ;EAAS,CAAA;EAAyBD,EAAA,KAAAC;EAAAD,EAAA,KAAAa;EAAAb,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAAA,OAA3Ec;AAA2E;AAI/E,MAAaC,6BACXC,cACM;CACN,MAAME,UAAUpC,YAAWqC,OAAAC,QAAA;EAAA,MAAApB,IAAAtB,EAAA,CAAA;EAGL,MAAAqB,KAAAoB;EAAY,IAAAjB;EAAA,IAAAF,EAAA,OAAAoB,OAAApB,EAAA,OAAAD,IAAA;GAD9BG,KAAA,oBAAC,uBAAD,EAAA,UACE,oBAAC,WAAD;IAAU,GAAMH;IAAoBqB;GAAG,CAAA,EADnB,CAAA;GAEEpB,EAAA,KAAAoB;GAAApB,EAAA,KAAAD;GAAAC,EAAA,KAAAE;EAAA,OAAAA,KAAAF,EAAA;EAAA,OAFxBE;CAEwB,CAE3B;CACDgB,QAAQG,cAAcL,UAAUK;CAChC,OAAOH;AACT;AAQA,SAAAX,iBAAAe,SAAA;CACE,MAAAT,UAAgB7B,WAAWW,aAAa;CACxC,IAAI,CAAC2B,SAAOjB,YAAR,CAAuBQ,SACzB,MAAM,IAAIU,MACR,6DACF;CAAE,OACGV;AAAO;AAGhB,MAAa,EAAEnB,iBAAiB8B,yBAAyBhC,uBACvDe,kBACA,iBACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSmooth.d.ts","names":[],"sources":["../../../src/utils/smooth/useSmooth.ts"],"mappings":";;;;;KAkBY;;;;;;EAMV;;;;;;EAMA;;;;;EAKA;;;;;;;;EAQA;;;;;;;;;;;;;;;;;;;;;;cA0GW,YACX,OAAO,oBAAoB,kBAAkB,uBAC7C,mBAAkB,kBACjB,oBAAoB,kBAAkB"}
1
+ {"version":3,"file":"useSmooth.d.ts","names":[],"sources":["../../../src/utils/smooth/useSmooth.ts"],"mappings":";;;;;KAkBY;;;;;;EAMV;;;;;;EAMA;;;;;EAKA;;;;;;;;EAQA;;;;;;;;;;;;;;;;;;;;;;cA6GW,YACX,OAAO,oBAAoB,kBAAkB,uBAC7C,mBAAkB,kBACjB,oBAAoB,kBAAkB"}
@@ -9,8 +9,6 @@ import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
9
9
  const DEFAULT_DRAIN_MS = 250;
10
10
  const DEFAULT_MAX_CHAR_INTERVAL_MS = 5;
11
11
  var TextStreamAnimator = class {
12
- currentText;
13
- setText;
14
12
  animationFrameId = null;
15
13
  lastUpdateTime = Date.now();
16
14
  lastCommitTime = 0;
@@ -19,6 +17,8 @@ var TextStreamAnimator = class {
19
17
  maxCharIntervalMs = DEFAULT_MAX_CHAR_INTERVAL_MS;
20
18
  maxCharsPerFrame = Infinity;
21
19
  minCommitMs = 0;
20
+ currentText;
21
+ setText;
22
22
  constructor(currentText, setText) {
23
23
  this.currentText = currentText;
24
24
  this.setText = setText;
@@ -90,7 +90,7 @@ const useSmooth = (state, smooth = false) => {
90
90
  const minCommitMs = positiveOr(options?.minCommitMs, 0);
91
91
  const [displayedText, setDisplayedText] = useState(state.status.type === "running" ? "" : text);
92
92
  const aui = useAui();
93
- const part = useAuiState(() => aui.part());
93
+ const part = useAuiState(() => aui.part);
94
94
  const [prevPart, setPrevPart] = useState(part);
95
95
  if (part !== prevPart || !text.startsWith(displayedText)) {
96
96
  setPrevPart(part);
@@ -1 +1 @@
1
- {"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useMediaQuery","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","minCommitMs","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","lastCommitTime","targetText","Infinity","constructor","currentText","setText","newText","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","isComplete","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","reduceMotion","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useMediaQuery } from \"../hooks/useMediaQuery\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n /**\n * Minimum time in milliseconds between committed updates. The reveal keeps\n * advancing every frame, but the visible text (and the downstream re-render\n * and markdown re-parse it triggers) is committed at most once per interval.\n * The final frame always commits. `0` commits every frame.\n * @default 0\n */\n minCommitMs?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n public lastCommitTime: number = 0;\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\n public minCommitMs: number = 0;\n\n constructor(\n public currentText: string,\n private setText: (newText: string) => void,\n ) {}\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n\n const isComplete = charsToAdd === remainingChars;\n if (isComplete || currentTime - this.lastCommitTime >= this.minCommitMs) {\n this.lastCommitTime = currentTime;\n this.setText(this.currentText);\n }\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n *\n * The reveal auto-disables under `prefers-reduced-motion: reduce`,\n * committing the full text immediately; this takes precedence over an\n * explicit `smooth={true}`.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const reduceMotion = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null && !reduceMotion;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n const minCommitMs = positiveOr(options?.minCommitMs, 0);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part());\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n animatorRef.minCommitMs = minCommitMs;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame, minCommitMs]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n // Discontinuity: part flipped, or new text breaks continuation\n // of the animator's current target. Either case requires\n // resetting the cursor — without the part check, a new part\n // whose text happens to share a prefix with the previous target\n // would keep the stale cursor and flicker.\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.lastCommitTime = 0;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n }\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":";;;;;;;;AA8CA,MAAMmB,mBAAmB;AACzB,MAAMC,+BAA+B;AAErC,IAAMC,qBAAN,MAAyB;CAYdS;CACCC;CAZV,mBAA0C;CAC1C,iBAAiCP,KAAKC,IAAI;CAC1C,iBAAgC;CAEhC,aAA4B;CAC5B,UAAyBN;CACzB,oBAAmCC;CACnC,mBAAkCQ;CAClC,cAA6B;CAE7BC,YACE,aACA,SACA;EAFOC,KAAAA,cAAAA;EACCC,KAAAA,UAAAA;CACP;CAEHE,QAAQ;EACN,IAAI,KAAKX,qBAAqB,MAAM;EACpC,KAAKC,iBAAiBC,KAAKC,IAAI;EAC/B,KAAKS,QAAQ;CACf;CAEAC,OAAO;EACL,IAAI,KAAKb,qBAAqB,MAAM;GAClCc,qBAAqB,KAAKd,gBAAgB;GAC1C,KAAKA,mBAAmB;EAC1B;CACF;CAEA,gBAAwB;EACtB,MAAMe,cAAcb,KAAKC,IAAI;EAE7B,IAAIc,gBADcF,cAAc,KAAKd;EAGrC,MAAMiB,iBAAiB,KAAKb,WAAWc,SAAS,KAAKX,YAAYW;EACjE,MAAMC,kBAAkBC,KAAKC,IAC3B,KAAK5B,mBACL,KAAKD,UAAUyB,cACjB;EAEA,MAAMK,aAAaF,KAAKC,IAAIJ,gBAAgB,KAAKvB,gBAAgB;EACjE,IAAI6B,aAAa;EACjB,OAAOP,iBAAiBG,mBAAmBI,aAAaD,YAAY;GAClEC;GACAP,iBAAiBG;EACnB;EAGA,IAAII,eAAeD,cAAcA,eAAe,KAAK5B,kBACnDsB,gBAAgB;EAGlB,IAAIO,eAAeN,gBACjB,KAAKlB,mBAAmByB,sBAAsB,KAAKb,OAAO;OAE1D,KAAKZ,mBAAmB;EAE1B,IAAIwB,eAAe,GAAG;EAEtB,KAAKhB,cAAc,KAAKH,WAAWqB,MACjC,GACA,KAAKlB,YAAYW,SAASK,UAC5B;EACA,KAAKvB,iBAAiBc,cAAcE;EAGpC,IADmBO,eAAeN,kBAChBH,cAAc,KAAKX,kBAAkB,KAAKR,aAAa;GACvE,KAAKQ,iBAAiBW;GACtB,KAAKN,QAAQ,KAAKD,WAAW;EAC/B;CACF;AACF;AAEA,MAAMoB,gBAAmCC,OAAOC,OAAO,EACrDC,MAAM,UACR,CAAC;AAED,MAAMC,cAAcC,OAA2BC,aAC7CD,UAAUE,KAAAA,KAAaF,QAAQ,IAAIA,QAAQC;;;;;;;;;;;;;;;;;;;;;AAsB7C,MAAaE,aACXC,OACAC,SAAkC,UAC8B;CAChE,MAAM,EAAEC,SAASF;CACjB,MAAMG,eAAenD,cAAc,kCAAkC;CACrE,MAAMoD,UACJ,OAAOH,WAAW,YAAYA,WAAW,OAAOA,SAASH,KAAAA;CAC3D,MAAMO,UAAUJ,WAAW,SAASA,WAAW,QAAQ,CAACE;CACxD,MAAM/C,UAAUuC,WAAWS,SAAShD,SAASI,gBAAgB;CAC7D,MAAMH,oBAAoBsC,WACxBS,SAAS/C,mBACTI,4BACF;CACA,MAAMH,mBAAmBqC,WAAWS,SAAS9C,kBAAkBW,QAAQ;CACvE,MAAMV,cAAcoC,WAAWS,SAAS7C,aAAa,CAAC;CAEtD,MAAM,CAAC+C,eAAeC,oBAAoB/D,SACxCwD,MAAMQ,OAAOd,SAAS,YAAY,KAAKQ,IACzC;CAWA,MAAMO,MAAMhE,OAAO;CACnB,MAAMiE,OAAOhE,kBAAkB+D,IAAIC,KAAK,CAAC;CACzC,MAAM,CAACC,UAAUC,eAAepE,SAASkE,IAAI;CAC7C,IAAIA,SAASC,YAAY,CAACT,KAAKW,WAAWP,aAAa,GAAG;EACxDM,YAAYF,IAAI;EAChBH,iBAAiBP,MAAMQ,OAAOd,SAAS,YAAY,KAAKQ,IAAI;CAC9D;CAEA,MAAMY,oBAAoB7D,qBAAqB,EAAE8D,UAAU,KAAK,CAAC;CACjE,MAAM3C,UAAUrB,gBAAgBmD,WAAiB;EAC/CK,iBAAiBL,MAAI;EACrB,IAAIY,mBAAmB;GACrB,MAAME,SACJV,kBAAkBJ,UAAQF,MAAMQ,OAAOd,SAAS,YAC5CH,gBACAS,MAAMQ;GACZtD,cAAc4D,iBAAiB,CAAC,CAACG,SAASD,QAAQ,IAAI;EACxD;CACF,CAAC;CAGD3E,gBAAgB;EACd,IAAIyE,mBAAmB;GACrB,MAAME,WACJX,YAAYC,kBAAkBJ,QAAQF,MAAMQ,OAAOd,SAAS,aACxDH,gBACAS,MAAMQ;GACZtD,cAAc4D,iBAAiB,CAAC,CAACG,SAASD,UAAQ,IAAI;EACxD;CACF,GAAG;EAACF;EAAmBT;EAASH;EAAMI;EAAeN,MAAMQ;CAAM,CAAC;CAElE,MAAM,CAACU,eAAe1E,SACpB,IAAIkB,mBAAmB4C,eAAelC,OAAO,CAC/C;CAEA/B,gBAAgB;EACd6E,YAAY9D,UAAUA;EACtB8D,YAAY7D,oBAAoBA;EAChC6D,YAAY5D,mBAAmBA;EAC/B4D,YAAY3D,cAAcA;CAC5B,GAAG;EAAC2D;EAAa9D;EAASC;EAAmBC;EAAkBC;CAAW,CAAC;CAE3E,MAAM4D,kBAAkB5E,OAAOmE,IAAI;CACnCrE,gBAAgB;EACd,IAAI,CAACgE,SAAS;GACZa,YAAY1C,KAAK;GACjB;EACF;EAOA,MAAM4C,cAAcD,gBAAgBE,YAAYX;EAChDS,gBAAgBE,UAAUX;EAC1B,IAAIU,eAAe,CAAClB,KAAKW,WAAWK,YAAYlD,UAAU,GAAG;GAC3D,IAAIgC,MAAMQ,OAAOd,SAAS,WAAW;IACnCwB,YAAY/C,cAAc;IAC1B+C,YAAYlD,aAAakC;IACzBgB,YAAYnD,iBAAiB;IAC7BmD,YAAY5C,MAAM;GACpB,OAAO;IACL4C,YAAY/C,cAAc+B;IAC1BgB,YAAYlD,aAAakC;IACzBgB,YAAY1C,KAAK;GACnB;GACA;EACF;EAEA0C,YAAYlD,aAAakC;EACzBgB,YAAY5C,MAAM;CACpB,GAAG;EAAC4C;EAAab;EAASH;EAAMF,MAAMQ,OAAOd;EAAMgB;CAAI,CAAC;CAExDrE,gBAAgB;EACd,aAAa;GACX6E,YAAY1C,KAAK;EACnB;CACF,GAAG,CAAC0C,WAAW,CAAC;CAEhB,OAAO5E,cAEH+D,UACI;EACE,GAAGL;EACHE,MAAMI;EACNE,QAAQN,SAASI,gBAAgBN,MAAMQ,SAASjB;CAClD,IACAS,OACN;EAACK;EAASC;EAAeN;EAAOE;CAAI,CACtC;AACF"}
1
+ {"version":3,"file":"useSmooth.js","names":["useEffect","useMemo","useRef","useState","useAui","useAuiState","MessagePartStatus","ReasoningMessagePart","TextMessagePart","MessagePartState","useCallbackRef","useMediaQuery","useSmoothStatusStore","writableStore","SmoothOptions","drainMs","maxCharIntervalMs","maxCharsPerFrame","minCommitMs","DEFAULT_DRAIN_MS","DEFAULT_MAX_CHAR_INTERVAL_MS","TextStreamAnimator","animationFrameId","lastUpdateTime","Date","now","lastCommitTime","targetText","Infinity","currentText","setText","newText","constructor","start","animate","stop","cancelAnimationFrame","currentTime","deltaTime","timeToConsume","remainingChars","length","baseTimePerChar","Math","min","frameLimit","charsToAdd","requestAnimationFrame","slice","isComplete","SMOOTH_STATUS","Object","freeze","type","positiveOr","value","fallback","undefined","useSmooth","state","smooth","text","reduceMotion","options","enabled","displayedText","setDisplayedText","status","aui","part","prevPart","setPrevPart","startsWith","smoothStatusStore","optional","target","setState","animatorRef","animatorPartRef","partChanged","current"],"sources":["../../../src/utils/smooth/useSmooth.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport type {\n MessagePartStatus,\n ReasoningMessagePart,\n TextMessagePart,\n MessagePartState,\n} from \"@assistant-ui/core\";\nimport { useCallbackRef } from \"@radix-ui/react-use-callback-ref\";\nimport { useMediaQuery } from \"../hooks/useMediaQuery\";\nimport { useSmoothStatusStore } from \"./SmoothContext\";\nimport { writableStore } from \"../../context/ReadonlyStore\";\n\n/**\n * Tuning options for the smooth text streaming animation.\n */\nexport type SmoothOptions = {\n /**\n * Target time in milliseconds to drain the backlog of unrevealed\n * characters. Larger values reveal long backlogs more gradually.\n * @default 250\n */\n drainMs?: number | undefined;\n /**\n * Maximum time in milliseconds between revealed characters, i.e. the\n * slowest reveal rate when the backlog is short.\n * @default 5\n */\n maxCharIntervalMs?: number | undefined;\n /**\n * Maximum number of characters revealed per animation frame.\n * @default Infinity\n */\n maxCharsPerFrame?: number | undefined;\n /**\n * Minimum time in milliseconds between committed updates. The reveal keeps\n * advancing every frame, but the visible text (and the downstream re-render\n * and markdown re-parse it triggers) is committed at most once per interval.\n * The final frame always commits. `0` commits every frame.\n * @default 0\n */\n minCommitMs?: number | undefined;\n};\n\nconst DEFAULT_DRAIN_MS = 250;\nconst DEFAULT_MAX_CHAR_INTERVAL_MS = 5;\n\nclass TextStreamAnimator {\n private animationFrameId: number | null = null;\n private lastUpdateTime: number = Date.now();\n public lastCommitTime: number = 0;\n\n public targetText: string = \"\";\n public drainMs: number = DEFAULT_DRAIN_MS;\n public maxCharIntervalMs: number = DEFAULT_MAX_CHAR_INTERVAL_MS;\n public maxCharsPerFrame: number = Infinity;\n public minCommitMs: number = 0;\n\n public currentText: string;\n private setText: (newText: string) => void;\n\n constructor(currentText: string, setText: (newText: string) => void) {\n this.currentText = currentText;\n this.setText = setText;\n }\n\n start() {\n if (this.animationFrameId !== null) return;\n this.lastUpdateTime = Date.now();\n this.animate();\n }\n\n stop() {\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n }\n\n private animate = () => {\n const currentTime = Date.now();\n const deltaTime = currentTime - this.lastUpdateTime;\n let timeToConsume = deltaTime;\n\n const remainingChars = this.targetText.length - this.currentText.length;\n const baseTimePerChar = Math.min(\n this.maxCharIntervalMs,\n this.drainMs / remainingChars,\n );\n\n const frameLimit = Math.min(remainingChars, this.maxCharsPerFrame);\n let charsToAdd = 0;\n while (timeToConsume >= baseTimePerChar && charsToAdd < frameLimit) {\n charsToAdd++;\n timeToConsume -= baseTimePerChar;\n }\n // A cap-limited frame must not bank its surplus time, or the next\n // frame would burst past the cap.\n if (charsToAdd === frameLimit && frameLimit === this.maxCharsPerFrame) {\n timeToConsume = 0;\n }\n\n if (charsToAdd !== remainingChars) {\n this.animationFrameId = requestAnimationFrame(this.animate);\n } else {\n this.animationFrameId = null;\n }\n if (charsToAdd === 0) return;\n\n this.currentText = this.targetText.slice(\n 0,\n this.currentText.length + charsToAdd,\n );\n this.lastUpdateTime = currentTime - timeToConsume;\n\n const isComplete = charsToAdd === remainingChars;\n if (isComplete || currentTime - this.lastCommitTime >= this.minCommitMs) {\n this.lastCommitTime = currentTime;\n this.setText(this.currentText);\n }\n };\n}\n\nconst SMOOTH_STATUS: MessagePartStatus = Object.freeze({\n type: \"running\",\n});\n\nconst positiveOr = (value: number | undefined, fallback: number): number =>\n value !== undefined && value > 0 ? value : fallback;\n\n/**\n * Animates streamed message part text with a typewriter-style reveal.\n *\n * Takes the current part state and a `smooth` argument: `false` disables,\n * `true` uses the default rate, and a {@link SmoothOptions} object tunes\n * the reveal. Returns the part state with `text` replaced by the revealed\n * prefix and `status` reporting `running` until the reveal catches up.\n *\n * The reveal auto-disables under `prefers-reduced-motion: reduce`,\n * committing the full text immediately; this takes precedence over an\n * explicit `smooth={true}`.\n *\n * @example\n * ```tsx\n * const { text, status } = useSmooth(useMessagePartText(), {\n * drainMs: 500,\n * maxCharsPerFrame: 30,\n * });\n * ```\n */\nexport const useSmooth = (\n state: MessagePartState & (TextMessagePart | ReasoningMessagePart),\n smooth: boolean | SmoothOptions = false,\n): MessagePartState & (TextMessagePart | ReasoningMessagePart) => {\n const { text } = state;\n const reduceMotion = useMediaQuery(\"(prefers-reduced-motion: reduce)\");\n const options =\n typeof smooth === \"object\" && smooth !== null ? smooth : undefined;\n const enabled = smooth !== false && smooth !== null && !reduceMotion;\n const drainMs = positiveOr(options?.drainMs, DEFAULT_DRAIN_MS);\n const maxCharIntervalMs = positiveOr(\n options?.maxCharIntervalMs,\n DEFAULT_MAX_CHAR_INTERVAL_MS,\n );\n const maxCharsPerFrame = positiveOr(options?.maxCharsPerFrame, Infinity);\n const minCommitMs = positiveOr(options?.minCommitMs, 0);\n\n const [displayedText, setDisplayedText] = useState(\n state.status.type === \"running\" ? \"\" : text,\n );\n\n // Render-phase resync on part flip or text discontinuity, so the\n // first paint after a thread switch never shows the previous\n // part's text (#4051). `displayedText` is already a prefix of\n // `text` during normal streaming, so use it as the previous-text\n // reference instead of carrying separate state — avoids the\n // double render per streaming token. Read part identity through\n // `useAuiState` so we actually subscribe to its changes instead\n // of relying on a render-time proxy reference that may be stable\n // across thread swaps.\n const aui = useAui();\n const part = useAuiState(() => aui.part);\n const [prevPart, setPrevPart] = useState(part);\n if (part !== prevPart || !text.startsWith(displayedText)) {\n setPrevPart(part);\n setDisplayedText(state.status.type === \"running\" ? \"\" : text);\n }\n\n const smoothStatusStore = useSmoothStatusStore({ optional: true });\n const setText = useCallbackRef((text: string) => {\n setDisplayedText(text);\n if (smoothStatusStore) {\n const target =\n displayedText !== text || state.status.type === \"running\"\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n });\n\n // TODO this is hacky\n useEffect(() => {\n if (smoothStatusStore) {\n const target =\n enabled && (displayedText !== text || state.status.type === \"running\")\n ? SMOOTH_STATUS\n : state.status;\n writableStore(smoothStatusStore).setState(target, true);\n }\n }, [smoothStatusStore, enabled, text, displayedText, state.status]);\n\n const [animatorRef] = useState<TextStreamAnimator>(\n new TextStreamAnimator(displayedText, setText),\n );\n\n useEffect(() => {\n animatorRef.drainMs = drainMs;\n animatorRef.maxCharIntervalMs = maxCharIntervalMs;\n animatorRef.maxCharsPerFrame = maxCharsPerFrame;\n animatorRef.minCommitMs = minCommitMs;\n }, [animatorRef, drainMs, maxCharIntervalMs, maxCharsPerFrame, minCommitMs]);\n\n const animatorPartRef = useRef(part);\n useEffect(() => {\n if (!enabled) {\n animatorRef.stop();\n return;\n }\n\n // Discontinuity: part flipped, or new text breaks continuation\n // of the animator's current target. Either case requires\n // resetting the cursor — without the part check, a new part\n // whose text happens to share a prefix with the previous target\n // would keep the stale cursor and flicker.\n const partChanged = animatorPartRef.current !== part;\n animatorPartRef.current = part;\n if (partChanged || !text.startsWith(animatorRef.targetText)) {\n if (state.status.type === \"running\") {\n animatorRef.currentText = \"\";\n animatorRef.targetText = text;\n animatorRef.lastCommitTime = 0;\n animatorRef.start();\n } else {\n animatorRef.currentText = text;\n animatorRef.targetText = text;\n animatorRef.stop();\n }\n return;\n }\n\n animatorRef.targetText = text;\n animatorRef.start();\n }, [animatorRef, enabled, text, state.status.type, part]);\n\n useEffect(() => {\n return () => {\n animatorRef.stop();\n };\n }, [animatorRef]);\n\n return useMemo(\n () =>\n enabled\n ? {\n ...state,\n text: displayedText,\n status: text === displayedText ? state.status : SMOOTH_STATUS,\n }\n : state,\n [enabled, displayedText, state, text],\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.14.28",
3
+ "version": "0.15.0",
4
4
  "description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
5
5
  "keywords": [
6
6
  "ai",
@@ -55,9 +55,9 @@
55
55
  ],
56
56
  "sideEffects": false,
57
57
  "dependencies": {
58
- "@assistant-ui/core": "^0.2.22",
59
- "@assistant-ui/store": "^0.2.21",
60
- "@assistant-ui/tap": "^0.9.5",
58
+ "@assistant-ui/core": "^0.3.0",
59
+ "@assistant-ui/store": "^0.3.0",
60
+ "@assistant-ui/tap": "^0.9.7",
61
61
  "@radix-ui/primitive": "^1.1.7",
62
62
  "@radix-ui/react-collection": "^1.1.15",
63
63
  "@radix-ui/react-compose-refs": "^1.1.5",
@@ -66,12 +66,12 @@
66
66
  "@radix-ui/react-use-callback-ref": "^1.1.4",
67
67
  "@radix-ui/react-use-controllable-state": "^1.2.6",
68
68
  "@radix-ui/react-use-escape-keydown": "^1.1.5",
69
- "assistant-cloud": "^0.1.36",
70
- "assistant-stream": "^0.3.27",
69
+ "assistant-cloud": "^0.1.37",
70
+ "assistant-stream": "^0.3.29",
71
71
  "nanoid": "^6.0.0",
72
72
  "radix-ui": "^1.6.7",
73
73
  "react-textarea-autosize": "^8.5.9",
74
- "safe-content-frame": "^0.0.24",
74
+ "safe-content-frame": "^0.0.25",
75
75
  "zod": "^4.4.3",
76
76
  "zustand": "^5.0.14"
77
77
  },
@@ -99,8 +99,8 @@
99
99
  "react": "^19.2.8",
100
100
  "react-dom": "^19.2.8",
101
101
  "vitest": "^4.1.10",
102
- "@assistant-ui/vite": "0.0.10",
103
- "@assistant-ui/x-buildutils": "0.0.19"
102
+ "@assistant-ui/vite": "0.0.11",
103
+ "@assistant-ui/x-buildutils": "0.0.20"
104
104
  },
105
105
  "publishConfig": {
106
106
  "access": "public",