@assistant-ui/react 0.15.25 → 0.15.27

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 (159) hide show
  1. package/dist/hooks/useToolCallElapsed.d.ts.map +1 -1
  2. package/dist/hooks/useToolCallElapsed.js +3 -2
  3. package/dist/hooks/useToolCallElapsed.js.map +1 -1
  4. package/dist/index.d.ts +31 -5
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  7. package/dist/mcp-apps/McpAppRenderer.js +3 -2
  8. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  9. package/dist/mcp-apps/app-frame.d.ts.map +1 -1
  10. package/dist/mcp-apps/app-frame.js +58 -48
  11. package/dist/mcp-apps/app-frame.js.map +1 -1
  12. package/dist/mcp-apps/bridge.d.ts.map +1 -1
  13. package/dist/mcp-apps/bridge.js +28 -3
  14. package/dist/mcp-apps/bridge.js.map +1 -1
  15. package/dist/mcp-apps/csp.d.ts +4 -0
  16. package/dist/mcp-apps/csp.d.ts.map +1 -0
  17. package/dist/mcp-apps/csp.js +93 -0
  18. package/dist/mcp-apps/csp.js.map +1 -0
  19. package/dist/mcp-apps/index.d.ts +1 -1
  20. package/dist/mcp-apps/index.d.ts.map +1 -1
  21. package/dist/mcp-apps/types.d.ts +19 -1
  22. package/dist/mcp-apps/types.d.ts.map +1 -1
  23. package/dist/mcp-apps/types.js.map +1 -1
  24. package/dist/primitives/assistantModal/AssistantModalRoot.d.ts +1 -0
  25. package/dist/primitives/assistantModal/AssistantModalRoot.d.ts.map +1 -1
  26. package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
  27. package/dist/primitives/attachment.d.ts +3 -1
  28. package/dist/primitives/attachment.d.ts.map +1 -1
  29. package/dist/primitives/attachment.js.map +1 -1
  30. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
  31. package/dist/primitives/composer/ComposerAttachmentDropzone.js +14 -12
  32. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  33. package/dist/primitives/composer/ComposerCancel.d.ts +5 -1
  34. package/dist/primitives/composer/ComposerCancel.d.ts.map +1 -1
  35. package/dist/primitives/composer/ComposerCancel.js +14 -1
  36. package/dist/primitives/composer/ComposerCancel.js.map +1 -1
  37. package/dist/primitives/composer/ComposerInput.d.ts +14 -0
  38. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  39. package/dist/primitives/composer/ComposerInput.js +4 -3
  40. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  41. package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
  42. package/dist/primitives/composer/ComposerRoot.js +24 -19
  43. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  44. package/dist/primitives/composer/composerInputSelector.d.ts +3 -0
  45. package/dist/primitives/composer/composerInputSelector.d.ts.map +1 -0
  46. package/dist/primitives/composer/composerInputSelector.js +7 -0
  47. package/dist/primitives/composer/composerInputSelector.js.map +1 -0
  48. package/dist/primitives/composer/useComposerCancelWithFocus.d.ts +2 -0
  49. package/dist/primitives/composer/useComposerCancelWithFocus.d.ts.map +1 -0
  50. package/dist/primitives/composer/useComposerCancelWithFocus.js +42 -0
  51. package/dist/primitives/composer/useComposerCancelWithFocus.js.map +1 -0
  52. package/dist/primitives/composer.d.ts +35 -14
  53. package/dist/primitives/composer.d.ts.map +1 -1
  54. package/dist/primitives/composer.js.map +1 -1
  55. package/dist/primitives/message/MessagePartsGrouped.d.ts +8 -1
  56. package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
  57. package/dist/primitives/message/MessagePartsGrouped.js +58 -278
  58. package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
  59. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.d.ts.map +1 -1
  60. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js +5 -2
  61. package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
  62. package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
  63. package/dist/primitives/thread/ThreadViewport.js +4 -3
  64. package/dist/primitives/thread/ThreadViewport.js.map +1 -1
  65. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js +2 -2
  66. package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
  67. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  68. package/dist/primitives/thread/useThreadViewportAutoScroll.js +90 -21
  69. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  70. package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
  71. package/dist/sandbox-host/SandboxHost.js +3 -2
  72. package/dist/sandbox-host/SandboxHost.js.map +1 -1
  73. package/dist/unstable/useComposerInput.d.ts +9 -4
  74. package/dist/unstable/useComposerInput.d.ts.map +1 -1
  75. package/dist/unstable/useComposerInput.js +6 -4
  76. package/dist/unstable/useComposerInput.js.map +1 -1
  77. package/dist/unstable/useComposerInputHistory.d.ts +3 -2
  78. package/dist/unstable/useComposerInputHistory.d.ts.map +1 -1
  79. package/dist/unstable/useComposerInputHistory.js +2 -2
  80. package/dist/unstable/useComposerInputHistory.js.map +1 -1
  81. package/dist/unstable/useLiveCompletionAdapter.d.ts +3 -2
  82. package/dist/unstable/useLiveCompletionAdapter.d.ts.map +1 -1
  83. package/dist/unstable/useLiveCompletionAdapter.js +8 -7
  84. package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
  85. package/dist/unstable/useMentionAdapter.d.ts +12 -3
  86. package/dist/unstable/useMentionAdapter.d.ts.map +1 -1
  87. package/dist/unstable/useMentionAdapter.js +2 -2
  88. package/dist/unstable/useMentionAdapter.js.map +1 -1
  89. package/dist/unstable/useMessageStallDetection.d.ts +4 -2
  90. package/dist/unstable/useMessageStallDetection.d.ts.map +1 -1
  91. package/dist/unstable/useMessageStallDetection.js +2 -2
  92. package/dist/unstable/useMessageStallDetection.js.map +1 -1
  93. package/dist/unstable/useSlashCommandAdapter.d.ts +5 -2
  94. package/dist/unstable/useSlashCommandAdapter.d.ts.map +1 -1
  95. package/dist/unstable/useSlashCommandAdapter.js +5 -4
  96. package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
  97. package/dist/unstable/webmcp/useWebMcpProvider.d.ts +4 -0
  98. package/dist/unstable/webmcp/useWebMcpProvider.d.ts.map +1 -1
  99. package/dist/unstable/webmcp/useWebMcpProvider.js +2 -0
  100. package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -1
  101. package/dist/utils/getSelectionMessageId.d.ts.map +1 -1
  102. package/dist/utils/getSelectionMessageId.js +31 -8
  103. package/dist/utils/getSelectionMessageId.js.map +1 -1
  104. package/dist/utils/hooks/useOnResizeContent.d.ts.map +1 -1
  105. package/dist/utils/hooks/useOnResizeContent.js +2 -22
  106. package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
  107. package/dist/utils/useIsomorphicLayoutEffect.d.ts +3 -0
  108. package/dist/utils/useIsomorphicLayoutEffect.d.ts.map +1 -0
  109. package/dist/utils/useIsomorphicLayoutEffect.js +6 -0
  110. package/dist/utils/useIsomorphicLayoutEffect.js.map +1 -0
  111. package/package.json +10 -10
  112. package/src/hooks/useToolCallElapsed.test.tsx +4 -2
  113. package/src/hooks/useToolCallElapsed.ts +3 -2
  114. package/src/index.ts +20 -2
  115. package/src/mcp-apps/McpAppRenderer.test.tsx +7 -2
  116. package/src/mcp-apps/McpAppRenderer.tsx +3 -9
  117. package/src/mcp-apps/app-frame.test.tsx +102 -6
  118. package/src/mcp-apps/app-frame.tsx +7 -10
  119. package/src/mcp-apps/bridge.test.ts +154 -0
  120. package/src/mcp-apps/bridge.ts +51 -9
  121. package/src/mcp-apps/csp.test.ts +112 -0
  122. package/src/mcp-apps/csp.ts +124 -0
  123. package/src/mcp-apps/index.ts +1 -0
  124. package/src/mcp-apps/types.ts +16 -1
  125. package/src/model-context/frame/AssistantFrame.test.ts +45 -0
  126. package/src/model-context/frame/SPEC_AssistantFrame.md +1 -0
  127. package/src/primitives/assistantModal/AssistantModalRoot.tsx +1 -0
  128. package/src/primitives/attachment.ts +4 -1
  129. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +32 -1
  130. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +14 -12
  131. package/src/primitives/composer/ComposerCancel.focus.test.tsx +307 -0
  132. package/src/primitives/composer/ComposerCancel.ts +8 -1
  133. package/src/primitives/composer/ComposerDisabledThread.test.tsx +121 -0
  134. package/src/primitives/composer/ComposerInput.tsx +19 -1
  135. package/src/primitives/composer/ComposerRoot.tsx +6 -5
  136. package/src/primitives/composer/composerInputSelector.ts +4 -0
  137. package/src/primitives/composer/useComposerCancelWithFocus.ts +57 -0
  138. package/src/primitives/composer.ts +56 -21
  139. package/src/primitives/message/MessagePartsGrouped.test.tsx +106 -2
  140. package/src/primitives/message/MessagePartsGrouped.tsx +36 -134
  141. package/src/primitives/selectionToolbar/SelectionToolbarRoot.test.tsx +20 -12
  142. package/src/primitives/selectionToolbar/SelectionToolbarRoot.tsx +6 -2
  143. package/src/primitives/thread/ThreadViewport.tsx +3 -3
  144. package/src/primitives/thread/topAnchor/useTopAnchorReserve.ts +2 -2
  145. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +666 -63
  146. package/src/primitives/thread/useThreadViewportAutoScroll.ts +201 -73
  147. package/src/sandbox-host/SandboxHost.tsx +3 -8
  148. package/src/unstable/useComposerInput.ts +9 -4
  149. package/src/unstable/useComposerInputHistory.ts +3 -2
  150. package/src/unstable/useLiveCompletionAdapter.ts +9 -14
  151. package/src/unstable/useMentionAdapter.ts +12 -3
  152. package/src/unstable/useMessageStallDetection.ts +4 -2
  153. package/src/unstable/useSlashCommandAdapter.ts +8 -4
  154. package/src/unstable/webmcp/useWebMcpProvider.ts +4 -0
  155. package/src/utils/getSelectionMessageId.test.ts +231 -0
  156. package/src/utils/getSelectionMessageId.ts +45 -7
  157. package/src/utils/hooks/useOnResizeContent.ts +2 -29
  158. package/src/utils/useIsomorphicLayoutEffect.test.tsx +21 -0
  159. package/src/utils/useIsomorphicLayoutEffect.ts +9 -0
@@ -1 +1 @@
1
- {"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing; the controlled value stays in\n * sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Headless bridge to the composer's text value and send action, for building a\n * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,\n * not a second input: it does not own keyboard behavior, autosize, IME or\n * contentEditable sync, paste/drop attachments, focus management, or rich-text\n * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for\n * trigger-popover combobox semantics.\n *\n * @example\n * ```tsx\n * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();\n * <textarea\n * value={value}\n * disabled={isDisabled}\n * onChange={(e) => setText(e.target.value)}\n * onKeyDown={(e) => {\n * if (e.key === \"Enter\" && !e.shiftKey && canSend) {\n * e.preventDefault();\n * send();\n * }\n * }}\n * />\n * ```\n */\nexport function unstable_useComposerInput(\n options?: Unstable_UseComposerInputOptions,\n): Unstable_ComposerInput {\n const aui = useAui();\n const value = useComposerInputValue();\n const isDisabled = useComposerInputDisabled(options?.disabled);\n\n const setText = useCallback(\n (text: string) => {\n if (!aui.composer.getState().isEditing) return;\n flushTapSync(() => {\n aui.composer.setText(text);\n });\n },\n [aui],\n );\n\n const { send: rawSend, disabled: sendDisabled } = useComposerSend();\n const canSend = !sendDisabled && !isDisabled;\n const send = useCallback(\n (sendOptions?: ComposerSendOptions) => {\n if (!canSend) return;\n rawSend(sendOptions);\n },\n [canSend, rawSend],\n );\n\n return { value, setText, send, isDisabled, canSend };\n}\n\nexport type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * ARIA combobox attributes for the focused element (typically the composer\n * input) describing the open trigger popover, per the WAI-ARIA editable\n * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or\n * when no popover is open. Spread these last so they take precedence over any\n * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.\n *\n * @example\n * ```tsx\n * const aria = unstable_useTriggerPopoverAriaProps();\n * <textarea {...aria} />\n * ```\n */\nexport function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps {\n return useTriggerPopoverAriaProps();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,SAAgBmB,0BACdH,SACwB;CACxB,MAAMI,MAAMnB,OAAO;CACnB,MAAMW,QAAQL,sBAAsB;CACpC,MAAMU,aAAaX,yBAAyBU,SAASN,QAAQ;CAE7D,MAAMG,UAAUb,aACbc,SAAiB;EAChB,IAAI,CAACM,IAAIC,SAASC,SAAS,CAAC,CAACC,WAAW;EACxCrB,mBAAmB;GACjBkB,IAAIC,SAASR,QAAQC,IAAI;EAC3B,CAAC;CACH,GACA,CAACM,GAAG,CACN;CAEA,MAAM,EAAEL,MAAMS,SAASd,UAAUe,iBAAiBtB,gBAAgB;CAClE,MAAMe,UAAU,CAACO,gBAAgB,CAACR;CASlC,OAAO;EAAEL;EAAOC;EAASE,MARZf,aACV0B,gBAAsC;GACrC,IAAI,CAACR,SAAS;GACdM,QAAQE,WAAW;EACrB,GACA,CAACR,SAASM,OAAO,CAGMT;EAAME;EAAYC;CAAQ;AACrD;;;;;;;;;;;;;;;;AAmBA,SAAgBU,sCAAwE;CACtF,OAAOpB,2BAA2B;AACpC"}
1
+ {"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing; the controlled value stays in\n * sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n * Headless bridge to the composer's text value and send action, for building a\n * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,\n * not a second input: it does not own keyboard behavior, autosize, IME or\n * contentEditable sync, paste/drop attachments, focus management, or rich-text\n * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for\n * trigger-popover combobox semantics.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();\n * <textarea\n * value={value}\n * disabled={isDisabled}\n * onChange={(e) => setText(e.target.value)}\n * onKeyDown={(e) => {\n * if (e.nativeEvent.isComposing || e.keyCode === 229) return;\n *\n * if (e.key === \"Enter\" && !e.shiftKey && canSend) {\n * e.preventDefault();\n * send();\n * }\n * }}\n * />\n * ```\n */\nexport function unstable_useComposerInput(\n options?: Unstable_UseComposerInputOptions,\n): Unstable_ComposerInput {\n const aui = useAui();\n const value = useComposerInputValue();\n const isDisabled = useComposerInputDisabled(options?.disabled);\n\n const setText = useCallback(\n (text: string) => {\n if (!aui.composer.getState().isEditing) return;\n flushTapSync(() => {\n aui.composer.setText(text);\n });\n },\n [aui],\n );\n\n const { send: rawSend, disabled: sendDisabled } = useComposerSend();\n const canSend = !sendDisabled && !isDisabled;\n const send = useCallback(\n (sendOptions?: ComposerSendOptions) => {\n if (!canSend) return;\n rawSend(sendOptions);\n },\n [canSend, rawSend],\n );\n\n return { value, setText, send, isDisabled, canSend };\n}\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;\n\n/**\n * ARIA combobox attributes for the focused element (typically the composer\n * input) describing the open trigger popover, per the WAI-ARIA editable\n * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or\n * when no popover is open. Spread these last so they take precedence over any\n * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const aria = unstable_useTriggerPopoverAriaProps();\n * <textarea {...aria} />\n * ```\n */\nexport function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps {\n return useTriggerPopoverAriaProps();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,SAAgBmB,0BACdH,SACwB;CACxB,MAAMI,MAAMnB,OAAO;CACnB,MAAMW,QAAQL,sBAAsB;CACpC,MAAMU,aAAaX,yBAAyBU,SAASN,QAAQ;CAE7D,MAAMG,UAAUb,aACbc,SAAiB;EAChB,IAAI,CAACM,IAAIC,SAASC,SAAS,CAAC,CAACC,WAAW;EACxCrB,mBAAmB;GACjBkB,IAAIC,SAASR,QAAQC,IAAI;EAC3B,CAAC;CACH,GACA,CAACM,GAAG,CACN;CAEA,MAAM,EAAEL,MAAMS,SAASd,UAAUe,iBAAiBtB,gBAAgB;CAClE,MAAMe,UAAU,CAACO,gBAAgB,CAACR;CASlC,OAAO;EAAEL;EAAOC;EAASE,MARZf,aACV0B,gBAAsC;GACrC,IAAI,CAACR,SAAS;GACdM,QAAQE,WAAW;EACrB,GACA,CAACR,SAASM,OAAO,CAGMT;EAAME;EAAYC;CAAQ;AACrD;;;;;;;;;;;;;;;;AAoBA,SAAgBU,sCAAwE;CACtF,OAAOpB,2BAA2B;AACpC"}
@@ -1,11 +1,10 @@
1
1
  import { type KeyboardEventHandler } from "react";
2
+ /** @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release. */
2
3
  export type Unstable_ComposerInputHistory = {
3
4
  /** Keydown handler to spread onto `ComposerPrimitive.Input`. */
4
5
  onKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;
5
6
  };
6
7
  /**
7
- * @deprecated Under active development and might change without notice.
8
- *
9
8
  * Terminal-style input history for the thread composer: ArrowUp on an
10
9
  * empty draft recalls previously sent user messages (newest first),
11
10
  * ArrowDown steps back toward the newest and finally restores the draft
@@ -17,6 +16,8 @@ export type Unstable_ComposerInputHistory = {
17
16
  * to modifier keys, and to consumer handlers that already called
18
17
  * `preventDefault`. It is inert on edit composers.
19
18
  *
19
+ * @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release.
20
+ *
20
21
  * @example
21
22
  * ```tsx
22
23
  * const history = unstable_useComposerInputHistory();
@@ -1 +1 @@
1
- {"version":3,"file":"useComposerInputHistory.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInputHistory.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AAOf,MAAM,MAAM,6BAA6B,GAAG;IAC1C,gEAAgE;IAChE,SAAS,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;CACtD,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gCAAgC,IAAI,6BAA6B,CA6FhF"}
1
+ {"version":3,"file":"useComposerInputHistory.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInputHistory.ts"],"names":[],"mappings":"AAEA,OAAO,EAML,KAAK,oBAAoB,EAC1B,MAAM,OAAO,CAAC;AAOf,+GAA+G;AAC/G,MAAM,MAAM,6BAA6B,GAAG;IAC1C,gEAAgE;IAChE,SAAS,EAAE,oBAAoB,CAAC,mBAAmB,CAAC,CAAC;CACtD,CAAC;AA2BF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gCAAgC,IAAI,6BAA6B,CA6FhF"}
@@ -20,8 +20,6 @@ const deriveHistory = (messages) => {
20
20
  const isOnFirstLine = (value, caret) => !value.slice(0, caret).includes("\n");
21
21
  const isOnLastLine = (value, caret) => !value.slice(caret).includes("\n");
22
22
  /**
23
- * @deprecated Under active development and might change without notice.
24
- *
25
23
  * Terminal-style input history for the thread composer: ArrowUp on an
26
24
  * empty draft recalls previously sent user messages (newest first),
27
25
  * ArrowDown steps back toward the newest and finally restores the draft
@@ -33,6 +31,8 @@ const isOnLastLine = (value, caret) => !value.slice(caret).includes("\n");
33
31
  * to modifier keys, and to consumer handlers that already called
34
32
  * `preventDefault`. It is inert on edit composers.
35
33
  *
34
+ * @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release.
35
+ *
36
36
  * @example
37
37
  * ```tsx
38
38
  * const history = unstable_useComposerInputHistory();
@@ -1 +1 @@
1
- {"version":3,"file":"useComposerInputHistory.js","names":["useCallback","useEffect","useMemo","useRef","KeyboardEvent","KeyboardEventHandler","useAui","flushTapSync","ThreadMessage","getThreadMessageText","useTriggerPopoverRootContextOptional","Unstable_ComposerInputHistory","onKeyDown","HTMLTextAreaElement","BrowseState","cursor","draftSnapshot","lastRecalledText","deriveHistory","messages","entries","i","length","message","role","text","trim","push","isOnFirstLine","value","caret","slice","includes","isOnLastLine","unstable_useComposerInputHistory","aui","popoverCtx","browseRef","composer","getState","type","undefined","on","current","e","defaultPrevented","key","nativeEvent","isComposing","shiftKey","ctrlKey","metaKey","altKey","getActiveAria","textarea","currentTarget","selectionStart","selectionEnd","browse","commitText","setText","requestAnimationFrame","setSelectionRange","preventDefault","recall","history","entry","thread","next"],"sources":["../../src/unstable/useComposerInputHistory.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type KeyboardEvent,\n type KeyboardEventHandler,\n} from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport type { ThreadMessage } from \"@assistant-ui/core\";\nimport { getThreadMessageText } from \"@assistant-ui/core/internal\";\nimport { useTriggerPopoverRootContextOptional } from \"../primitives/composer/trigger/TriggerPopoverRootContext\";\n\nexport type Unstable_ComposerInputHistory = {\n /** Keydown handler to spread onto `ComposerPrimitive.Input`. */\n onKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;\n};\n\ntype BrowseState = {\n cursor: number;\n draftSnapshot: string;\n lastRecalledText: string;\n};\n\nconst deriveHistory = (messages: readonly ThreadMessage[]): string[] => {\n const entries: string[] = [];\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]!;\n if (message.role !== \"user\") continue;\n const text = getThreadMessageText(message).trim();\n if (!text) continue;\n if (entries[entries.length - 1] === text) continue;\n entries.push(text);\n }\n return entries;\n};\n\nconst isOnFirstLine = (value: string, caret: number): boolean =>\n !value.slice(0, caret).includes(\"\\n\");\n\nconst isOnLastLine = (value: string, caret: number): boolean =>\n !value.slice(caret).includes(\"\\n\");\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Terminal-style input history for the thread composer: ArrowUp on an\n * empty draft recalls previously sent user messages (newest first),\n * ArrowDown steps back toward the newest and finally restores the draft\n * that was being typed when browsing started.\n *\n * Recall only triggers when the caret is on the first/last line with no\n * selection, so multi-line editing keeps native arrow behavior. The\n * handler yields to an open mention/slash popover, to IME composition,\n * to modifier keys, and to consumer handlers that already called\n * `preventDefault`. It is inert on edit composers.\n *\n * @example\n * ```tsx\n * const history = unstable_useComposerInputHistory();\n * <ComposerPrimitive.Input {...history} />\n * ```\n */\nexport function unstable_useComposerInputHistory(): Unstable_ComposerInputHistory {\n const aui = useAui();\n const popoverCtx = useTriggerPopoverRootContextOptional();\n const browseRef = useRef<BrowseState | null>(null);\n\n useEffect(() => {\n if (aui.composer.getState().type !== \"thread\") return undefined;\n\n return aui.on(\"threads.selectionChanged\", () => {\n browseRef.current = null;\n });\n }, [aui]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.defaultPrevented) return;\n if (e.key !== \"ArrowUp\" && e.key !== \"ArrowDown\") return;\n if (e.nativeEvent.isComposing) return;\n if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return;\n if (popoverCtx && popoverCtx.getActiveAria() !== null) return;\n if (aui.composer.getState().type !== \"thread\") return;\n\n const textarea = e.currentTarget;\n const { selectionStart, selectionEnd, value } = textarea;\n if (selectionStart !== selectionEnd) return;\n\n if (browseRef.current && value !== browseRef.current.lastRecalledText) {\n browseRef.current = null;\n }\n const browse = browseRef.current;\n\n const commitText = (text: string): void => {\n flushTapSync(() => aui.composer.setText(text));\n // React's controlled-value commit restores the pre-recall caret;\n // reposition after the commit, before paint.\n requestAnimationFrame(() => {\n textarea.setSelectionRange(text.length, text.length);\n });\n e.preventDefault();\n };\n\n const recall = (\n history: readonly string[],\n cursor: number,\n draftSnapshot: string,\n ): void => {\n const entry = history[cursor];\n if (entry === undefined) {\n e.preventDefault();\n return;\n }\n browseRef.current = { cursor, draftSnapshot, lastRecalledText: entry };\n commitText(entry);\n };\n\n if (e.key === \"ArrowUp\") {\n if (!isOnFirstLine(value, selectionStart)) return;\n\n if (!browse) {\n if (value.trim() !== \"\") return;\n const history = deriveHistory(aui.thread.getState().messages);\n if (history.length === 0) return;\n recall(history, 0, value);\n return;\n }\n\n const history = deriveHistory(aui.thread.getState().messages);\n const next = browse.cursor + 1;\n if (next >= history.length) {\n e.preventDefault();\n return;\n }\n recall(history, next, browse.draftSnapshot);\n return;\n }\n\n if (!browse) return;\n if (!isOnLastLine(value, selectionEnd)) return;\n\n const next = browse.cursor - 1;\n if (next < 0) {\n browseRef.current = null;\n commitText(browse.draftSnapshot);\n return;\n }\n\n const history = deriveHistory(aui.thread.getState().messages);\n recall(history, next, browse.draftSnapshot);\n },\n [aui, popoverCtx],\n );\n\n return useMemo(() => ({ onKeyDown }), [onKeyDown]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useComposerInputHistory.js","names":["useCallback","useEffect","useMemo","useRef","KeyboardEvent","KeyboardEventHandler","useAui","flushTapSync","ThreadMessage","getThreadMessageText","useTriggerPopoverRootContextOptional","Unstable_ComposerInputHistory","onKeyDown","HTMLTextAreaElement","BrowseState","cursor","draftSnapshot","lastRecalledText","deriveHistory","messages","entries","i","length","message","role","text","trim","push","isOnFirstLine","value","caret","slice","includes","isOnLastLine","unstable_useComposerInputHistory","aui","popoverCtx","browseRef","composer","getState","type","undefined","on","current","e","defaultPrevented","key","nativeEvent","isComposing","shiftKey","ctrlKey","metaKey","altKey","getActiveAria","textarea","currentTarget","selectionStart","selectionEnd","browse","commitText","setText","requestAnimationFrame","setSelectionRange","preventDefault","recall","history","entry","thread","next"],"sources":["../../src/unstable/useComposerInputHistory.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type KeyboardEvent,\n type KeyboardEventHandler,\n} from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport type { ThreadMessage } from \"@assistant-ui/core\";\nimport { getThreadMessageText } from \"@assistant-ui/core/internal\";\nimport { useTriggerPopoverRootContextOptional } from \"../primitives/composer/trigger/TriggerPopoverRootContext\";\n\n/** @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ComposerInputHistory = {\n /** Keydown handler to spread onto `ComposerPrimitive.Input`. */\n onKeyDown: KeyboardEventHandler<HTMLTextAreaElement>;\n};\n\ntype BrowseState = {\n cursor: number;\n draftSnapshot: string;\n lastRecalledText: string;\n};\n\nconst deriveHistory = (messages: readonly ThreadMessage[]): string[] => {\n const entries: string[] = [];\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i]!;\n if (message.role !== \"user\") continue;\n const text = getThreadMessageText(message).trim();\n if (!text) continue;\n if (entries[entries.length - 1] === text) continue;\n entries.push(text);\n }\n return entries;\n};\n\nconst isOnFirstLine = (value: string, caret: number): boolean =>\n !value.slice(0, caret).includes(\"\\n\");\n\nconst isOnLastLine = (value: string, caret: number): boolean =>\n !value.slice(caret).includes(\"\\n\");\n\n/**\n * Terminal-style input history for the thread composer: ArrowUp on an\n * empty draft recalls previously sent user messages (newest first),\n * ArrowDown steps back toward the newest and finally restores the draft\n * that was being typed when browsing started.\n *\n * Recall only triggers when the caret is on the first/last line with no\n * selection, so multi-line editing keeps native arrow behavior. The\n * handler yields to an open mention/slash popover, to IME composition,\n * to modifier keys, and to consumer handlers that already called\n * `preventDefault`. It is inert on edit composers.\n *\n * @deprecated Experimental since 2026-06-11. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const history = unstable_useComposerInputHistory();\n * <ComposerPrimitive.Input {...history} />\n * ```\n */\nexport function unstable_useComposerInputHistory(): Unstable_ComposerInputHistory {\n const aui = useAui();\n const popoverCtx = useTriggerPopoverRootContextOptional();\n const browseRef = useRef<BrowseState | null>(null);\n\n useEffect(() => {\n if (aui.composer.getState().type !== \"thread\") return undefined;\n\n return aui.on(\"threads.selectionChanged\", () => {\n browseRef.current = null;\n });\n }, [aui]);\n\n const onKeyDown = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.defaultPrevented) return;\n if (e.key !== \"ArrowUp\" && e.key !== \"ArrowDown\") return;\n if (e.nativeEvent.isComposing) return;\n if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return;\n if (popoverCtx && popoverCtx.getActiveAria() !== null) return;\n if (aui.composer.getState().type !== \"thread\") return;\n\n const textarea = e.currentTarget;\n const { selectionStart, selectionEnd, value } = textarea;\n if (selectionStart !== selectionEnd) return;\n\n if (browseRef.current && value !== browseRef.current.lastRecalledText) {\n browseRef.current = null;\n }\n const browse = browseRef.current;\n\n const commitText = (text: string): void => {\n flushTapSync(() => aui.composer.setText(text));\n // React's controlled-value commit restores the pre-recall caret;\n // reposition after the commit, before paint.\n requestAnimationFrame(() => {\n textarea.setSelectionRange(text.length, text.length);\n });\n e.preventDefault();\n };\n\n const recall = (\n history: readonly string[],\n cursor: number,\n draftSnapshot: string,\n ): void => {\n const entry = history[cursor];\n if (entry === undefined) {\n e.preventDefault();\n return;\n }\n browseRef.current = { cursor, draftSnapshot, lastRecalledText: entry };\n commitText(entry);\n };\n\n if (e.key === \"ArrowUp\") {\n if (!isOnFirstLine(value, selectionStart)) return;\n\n if (!browse) {\n if (value.trim() !== \"\") return;\n const history = deriveHistory(aui.thread.getState().messages);\n if (history.length === 0) return;\n recall(history, 0, value);\n return;\n }\n\n const history = deriveHistory(aui.thread.getState().messages);\n const next = browse.cursor + 1;\n if (next >= history.length) {\n e.preventDefault();\n return;\n }\n recall(history, next, browse.draftSnapshot);\n return;\n }\n\n if (!browse) return;\n if (!isOnLastLine(value, selectionEnd)) return;\n\n const next = browse.cursor - 1;\n if (next < 0) {\n browseRef.current = null;\n commitText(browse.draftSnapshot);\n return;\n }\n\n const history = deriveHistory(aui.thread.getState().messages);\n recall(history, next, browse.draftSnapshot);\n },\n [aui, popoverCtx],\n );\n\n return useMemo(() => ({ onKeyDown }), [onKeyDown]);\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { Unstable_TriggerAdapter, Unstable_TriggerItem } from "@assistant-ui/core";
2
+ /** @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release. */
2
3
  export type Unstable_UseLiveCompletionAdapterOptions = {
3
4
  /**
4
5
  * Fetches the items for a query from an async source. Called debounced; the
@@ -17,8 +18,6 @@ export type Unstable_UseLiveCompletionAdapterOptions = {
17
18
  readonly enabled?: boolean | undefined;
18
19
  };
19
20
  /**
20
- * @deprecated Under active development and may change without notice.
21
- *
22
21
  * Bridges an async completion source (a server search, a gateway RPC) into the
23
22
  * synchronous `Unstable_TriggerAdapter` that `ComposerTriggerPopover` consumes.
24
23
  * `search(query)` returns the last fetched items synchronously and schedules a
@@ -29,6 +28,8 @@ export type Unstable_UseLiveCompletionAdapterOptions = {
29
28
  * `isLoading` is `true` while a fetch is in flight. Pass it to the popover's
30
29
  * `isLoading` prop to render a loading state.
31
30
  *
31
+ * @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release.
32
+ *
32
33
  * @example
33
34
  * ```tsx
34
35
  * const mentions = unstable_useLiveCompletionAdapter({
@@ -1 +1 @@
1
- {"version":3,"file":"useLiveCompletionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useLiveCompletionAdapter.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,wCAAwC,GAAG;IACrD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC9E;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,qFAAqF;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAKF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,GAChD;IAAE,OAAO,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAiK1D"}
1
+ {"version":3,"file":"useLiveCompletionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useLiveCompletionAdapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,+GAA+G;AAC/G,MAAM,MAAM,wCAAwC,GAAG;IACrD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC9E;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,qFAAqF;IACrF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC,CAAC;AAKF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,wCAAwC,GAChD;IAAE,OAAO,EAAE,uBAAuB,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAiK1D"}
@@ -1,11 +1,10 @@
1
1
  "use client";
2
- import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
2
+ import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect.js";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from "@assistant-ui/tap/react-shim";
3
4
  //#region src/unstable/useLiveCompletionAdapter.ts
4
5
  /** Sentinel that no real query (including the empty string) equals, so the first query always fetches. */
5
6
  const NO_QUERY = "\0";
6
7
  /**
7
- * @deprecated Under active development and may change without notice.
8
- *
9
8
  * Bridges an async completion source (a server search, a gateway RPC) into the
10
9
  * synchronous `Unstable_TriggerAdapter` that `ComposerTriggerPopover` consumes.
11
10
  * `search(query)` returns the last fetched items synchronously and schedules a
@@ -16,6 +15,8 @@ const NO_QUERY = "\0";
16
15
  * `isLoading` is `true` while a fetch is in flight. Pass it to the popover's
17
16
  * `isLoading` prop to render a loading state.
18
17
  *
18
+ * @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release.
19
+ *
19
20
  * @example
20
21
  * ```tsx
21
22
  * const mentions = unstable_useLiveCompletionAdapter({
@@ -39,7 +40,7 @@ function unstable_useLiveCompletionAdapter(options) {
39
40
  });
40
41
  const [isLoading, setIsLoading] = useState(false);
41
42
  const fetcherRef = useRef(fetcher);
42
- useLayoutEffect(() => {
43
+ useIsomorphicLayoutEffect(() => {
43
44
  fetcherRef.current = fetcher;
44
45
  }, [fetcher]);
45
46
  const timerRef = useRef(null);
@@ -107,7 +108,7 @@ function unstable_useLiveCompletionAdapter(options) {
107
108
  rearmPendingRetry
108
109
  ]);
109
110
  const scheduleFetchRef = useRef(scheduleFetch);
110
- useLayoutEffect(() => {
111
+ useIsomorphicLayoutEffect(() => {
111
112
  scheduleFetchRef.current = scheduleFetch;
112
113
  }, [scheduleFetch]);
113
114
  const invalidatePending = useCallback(() => {
@@ -118,7 +119,7 @@ function unstable_useLiveCompletionAdapter(options) {
118
119
  setIsLoading(false);
119
120
  }, [cancelTimer, rearmPendingRetry]);
120
121
  const cacheKeyRef = useRef(cacheKey);
121
- useLayoutEffect(() => {
122
+ useIsomorphicLayoutEffect(() => {
122
123
  if (cacheKeyRef.current === cacheKey) return;
123
124
  cacheKeyRef.current = cacheKey;
124
125
  invalidatePending();
@@ -139,7 +140,7 @@ function unstable_useLiveCompletionAdapter(options) {
139
140
  failed: false
140
141
  });
141
142
  }, [enabled, invalidatePending]);
142
- useLayoutEffect(() => {
143
+ useIsomorphicLayoutEffect(() => {
143
144
  inactiveRef.current = false;
144
145
  const deferredQuery = deferredQueryRef.current;
145
146
  deferredQueryRef.current = null;
@@ -1 +1 @@
1
- {"version":3,"file":"useLiveCompletionAdapter.js","names":["useCallback","useEffect","useLayoutEffect","useMemo","useRef","useState","Unstable_TriggerAdapter","Unstable_TriggerItem","Unstable_UseLiveCompletionAdapterOptions","fetcher","query","Promise","cacheKey","debounceMs","enabled","NO_QUERY","unstable_useLiveCompletionAdapter","options","adapter","isLoading","state","setState","items","failed","setIsLoading","fetcherRef","current","timerRef","ReturnType","setTimeout","tokenRef","pendingQueryRef","retryableQueryRef","pendingRetryQueryRef","inactiveRef","deferredQueryRef","cancelTimer","clearTimeout","rearmPendingRetry","scheduleFetch","token","resolve","then","scheduleFetchRef","invalidatePending","cacheKeyRef","s","deferredQuery","categories","categoryItems","search","queueMicrotask"],"sources":["../../src/unstable/useLiveCompletionAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Unstable_TriggerAdapter,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\n\nexport type Unstable_UseLiveCompletionAdapterOptions = {\n /**\n * Fetches the items for a query from an async source. Called debounced; the\n * resolved items are cached and returned synchronously to the popover on the\n * next render.\n */\n readonly fetcher: (query: string) => Promise<readonly Unstable_TriggerItem[]>;\n /**\n * Identifies the fetcher's data source. Change this when switching accounts,\n * workspaces, or another boundary that should invalidate cached results.\n */\n readonly cacheKey?: string | number | undefined;\n /** Debounce applied before a fetch fires, in milliseconds. @default 60 */\n readonly debounceMs?: number | undefined;\n /** When `false`, no fetch is scheduled and the adapter stays empty. @default true */\n readonly enabled?: boolean | undefined;\n};\n\n/** Sentinel that no real query (including the empty string) equals, so the first query always fetches. */\nconst NO_QUERY = \"\\u0000\";\n\n/**\n * @deprecated Under active development and may change without notice.\n *\n * Bridges an async completion source (a server search, a gateway RPC) into the\n * synchronous `Unstable_TriggerAdapter` that `ComposerTriggerPopover` consumes.\n * `search(query)` returns the last fetched items synchronously and schedules a\n * debounced fetch when the query changes; when results arrive the returned\n * `adapter` identity changes, which re-runs the popover's lookup so the fresh\n * items render. This is a search-only adapter (`categories` are empty).\n *\n * `isLoading` is `true` while a fetch is in flight. Pass it to the popover's\n * `isLoading` prop to render a loading state.\n *\n * @example\n * ```tsx\n * const mentions = unstable_useLiveCompletionAdapter({\n * fetcher: (query) => searchUsers(query),\n * });\n *\n * <ComposerTriggerPopover\n * char=\"@\"\n * adapter={mentions.adapter}\n * isLoading={mentions.isLoading}\n * directive={{ onInserted }}\n * />\n * ```\n */\nexport function unstable_useLiveCompletionAdapter(\n options: Unstable_UseLiveCompletionAdapterOptions,\n): { adapter: Unstable_TriggerAdapter; isLoading: boolean } {\n const { fetcher, cacheKey, debounceMs = 60, enabled = true } = options;\n\n const [state, setState] = useState<{\n query: string;\n items: readonly Unstable_TriggerItem[];\n failed: boolean;\n }>({ query: NO_QUERY, items: [], failed: false });\n const [isLoading, setIsLoading] = useState(false);\n\n const fetcherRef = useRef(fetcher);\n // The debounce timer must only observe fetchers from committed renders.\n useLayoutEffect(() => {\n fetcherRef.current = fetcher;\n }, [fetcher]);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const tokenRef = useRef(0);\n const pendingQueryRef = useRef<string | null>(null);\n const retryableQueryRef = useRef<string | null>(null);\n const pendingRetryQueryRef = useRef<string | null>(null);\n const inactiveRef = useRef(true);\n const deferredQueryRef = useRef<string | null>(null);\n\n const cancelTimer = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const rearmPendingRetry = useCallback(() => {\n const query = pendingRetryQueryRef.current;\n if (query === null) return;\n retryableQueryRef.current = query;\n pendingRetryQueryRef.current = null;\n }, []);\n\n const scheduleFetch = useCallback(\n (query: string) => {\n if (!enabled) return;\n if (inactiveRef.current) {\n deferredQueryRef.current = query;\n return;\n }\n if (pendingQueryRef.current === query) return;\n rearmPendingRetry();\n if (retryableQueryRef.current === query) {\n retryableQueryRef.current = null;\n pendingRetryQueryRef.current = query;\n }\n pendingQueryRef.current = query;\n cancelTimer();\n const token = ++tokenRef.current;\n setIsLoading(true);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n Promise.resolve()\n .then(() => fetcherRef.current(query))\n .then(\n (items) => {\n if (token !== tokenRef.current) return;\n pendingRetryQueryRef.current = null;\n setState({ query, items, failed: false });\n setIsLoading(false);\n },\n () => {\n if (token !== tokenRef.current) return;\n pendingQueryRef.current = null;\n pendingRetryQueryRef.current = null;\n setState({ query, items: [], failed: true });\n setIsLoading(false);\n },\n );\n }, debounceMs);\n },\n [enabled, debounceMs, cancelTimer, rearmPendingRetry],\n );\n\n const scheduleFetchRef = useRef(scheduleFetch);\n useLayoutEffect(() => {\n scheduleFetchRef.current = scheduleFetch;\n }, [scheduleFetch]);\n\n const invalidatePending = useCallback(() => {\n rearmPendingRetry();\n cancelTimer();\n pendingQueryRef.current = null;\n tokenRef.current += 1;\n setIsLoading(false);\n }, [cancelTimer, rearmPendingRetry]);\n\n const cacheKeyRef = useRef(cacheKey);\n useLayoutEffect(() => {\n if (cacheKeyRef.current === cacheKey) return;\n cacheKeyRef.current = cacheKey;\n invalidatePending();\n retryableQueryRef.current = null;\n pendingRetryQueryRef.current = null;\n setState({ query: NO_QUERY, items: [], failed: false });\n }, [cacheKey, invalidatePending]);\n\n useEffect(() => {\n if (enabled) return;\n invalidatePending();\n setState((s) =>\n s.query === NO_QUERY ? s : { query: NO_QUERY, items: [], failed: false },\n );\n }, [enabled, invalidatePending]);\n\n // Render-time searches can outlive an abandoned render, so they only arm\n // request work after this hook commits.\n useLayoutEffect(() => {\n inactiveRef.current = false;\n const deferredQuery = deferredQueryRef.current;\n deferredQueryRef.current = null;\n if (deferredQuery !== null) scheduleFetchRef.current(deferredQuery);\n return () => {\n inactiveRef.current = true;\n invalidatePending();\n };\n }, [invalidatePending]);\n\n // Arm retries only after the failed state commits. Arming during rejection\n // would let the failure render immediately schedule another request.\n useEffect(() => {\n retryableQueryRef.current = state.failed ? state.query : null;\n }, [state]);\n\n const adapter = useMemo<Unstable_TriggerAdapter>(\n () => ({\n categories: () => [],\n categoryItems: () => [],\n search: (query: string) => {\n // search() runs inside the popover's render; defer state updates with\n // queueMicrotask so they are not dispatched while another component renders.\n if (query !== state.query || retryableQueryRef.current === query) {\n queueMicrotask(() => scheduleFetch(query));\n } else {\n queueMicrotask(() => {\n if (\n deferredQueryRef.current !== null &&\n deferredQueryRef.current !== query\n ) {\n deferredQueryRef.current = null;\n }\n if (\n pendingQueryRef.current !== null &&\n pendingQueryRef.current !== query\n ) {\n invalidatePending();\n }\n });\n }\n return state.items;\n },\n }),\n [state, scheduleFetch, invalidatePending],\n );\n\n return { adapter, isLoading };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useLiveCompletionAdapter.js","names":["useCallback","useEffect","useMemo","useRef","useState","useIsomorphicLayoutEffect","Unstable_TriggerAdapter","Unstable_TriggerItem","Unstable_UseLiveCompletionAdapterOptions","fetcher","query","Promise","cacheKey","debounceMs","enabled","NO_QUERY","unstable_useLiveCompletionAdapter","options","adapter","isLoading","state","setState","items","failed","setIsLoading","fetcherRef","current","timerRef","ReturnType","setTimeout","tokenRef","pendingQueryRef","retryableQueryRef","pendingRetryQueryRef","inactiveRef","deferredQueryRef","cancelTimer","clearTimeout","rearmPendingRetry","scheduleFetch","token","resolve","then","scheduleFetchRef","invalidatePending","cacheKeyRef","s","deferredQuery","categories","categoryItems","search","queueMicrotask"],"sources":["../../src/unstable/useLiveCompletionAdapter.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport type {\n Unstable_TriggerAdapter,\n Unstable_TriggerItem,\n} from \"@assistant-ui/core\";\n\n/** @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseLiveCompletionAdapterOptions = {\n /**\n * Fetches the items for a query from an async source. Called debounced; the\n * resolved items are cached and returned synchronously to the popover on the\n * next render.\n */\n readonly fetcher: (query: string) => Promise<readonly Unstable_TriggerItem[]>;\n /**\n * Identifies the fetcher's data source. Change this when switching accounts,\n * workspaces, or another boundary that should invalidate cached results.\n */\n readonly cacheKey?: string | number | undefined;\n /** Debounce applied before a fetch fires, in milliseconds. @default 60 */\n readonly debounceMs?: number | undefined;\n /** When `false`, no fetch is scheduled and the adapter stays empty. @default true */\n readonly enabled?: boolean | undefined;\n};\n\n/** Sentinel that no real query (including the empty string) equals, so the first query always fetches. */\nconst NO_QUERY = \"\\u0000\";\n\n/**\n * Bridges an async completion source (a server search, a gateway RPC) into the\n * synchronous `Unstable_TriggerAdapter` that `ComposerTriggerPopover` consumes.\n * `search(query)` returns the last fetched items synchronously and schedules a\n * debounced fetch when the query changes; when results arrive the returned\n * `adapter` identity changes, which re-runs the popover's lookup so the fresh\n * items render. This is a search-only adapter (`categories` are empty).\n *\n * `isLoading` is `true` while a fetch is in flight. Pass it to the popover's\n * `isLoading` prop to render a loading state.\n *\n * @deprecated Experimental since 2026-06-15. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const mentions = unstable_useLiveCompletionAdapter({\n * fetcher: (query) => searchUsers(query),\n * });\n *\n * <ComposerTriggerPopover\n * char=\"@\"\n * adapter={mentions.adapter}\n * isLoading={mentions.isLoading}\n * directive={{ onInserted }}\n * />\n * ```\n */\nexport function unstable_useLiveCompletionAdapter(\n options: Unstable_UseLiveCompletionAdapterOptions,\n): { adapter: Unstable_TriggerAdapter; isLoading: boolean } {\n const { fetcher, cacheKey, debounceMs = 60, enabled = true } = options;\n\n const [state, setState] = useState<{\n query: string;\n items: readonly Unstable_TriggerItem[];\n failed: boolean;\n }>({ query: NO_QUERY, items: [], failed: false });\n const [isLoading, setIsLoading] = useState(false);\n\n const fetcherRef = useRef(fetcher);\n // The debounce timer must only observe fetchers from committed renders.\n useIsomorphicLayoutEffect(() => {\n fetcherRef.current = fetcher;\n }, [fetcher]);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const tokenRef = useRef(0);\n const pendingQueryRef = useRef<string | null>(null);\n const retryableQueryRef = useRef<string | null>(null);\n const pendingRetryQueryRef = useRef<string | null>(null);\n const inactiveRef = useRef(true);\n const deferredQueryRef = useRef<string | null>(null);\n\n const cancelTimer = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const rearmPendingRetry = useCallback(() => {\n const query = pendingRetryQueryRef.current;\n if (query === null) return;\n retryableQueryRef.current = query;\n pendingRetryQueryRef.current = null;\n }, []);\n\n const scheduleFetch = useCallback(\n (query: string) => {\n if (!enabled) return;\n if (inactiveRef.current) {\n deferredQueryRef.current = query;\n return;\n }\n if (pendingQueryRef.current === query) return;\n rearmPendingRetry();\n if (retryableQueryRef.current === query) {\n retryableQueryRef.current = null;\n pendingRetryQueryRef.current = query;\n }\n pendingQueryRef.current = query;\n cancelTimer();\n const token = ++tokenRef.current;\n setIsLoading(true);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n Promise.resolve()\n .then(() => fetcherRef.current(query))\n .then(\n (items) => {\n if (token !== tokenRef.current) return;\n pendingRetryQueryRef.current = null;\n setState({ query, items, failed: false });\n setIsLoading(false);\n },\n () => {\n if (token !== tokenRef.current) return;\n pendingQueryRef.current = null;\n pendingRetryQueryRef.current = null;\n setState({ query, items: [], failed: true });\n setIsLoading(false);\n },\n );\n }, debounceMs);\n },\n [enabled, debounceMs, cancelTimer, rearmPendingRetry],\n );\n\n const scheduleFetchRef = useRef(scheduleFetch);\n useIsomorphicLayoutEffect(() => {\n scheduleFetchRef.current = scheduleFetch;\n }, [scheduleFetch]);\n\n const invalidatePending = useCallback(() => {\n rearmPendingRetry();\n cancelTimer();\n pendingQueryRef.current = null;\n tokenRef.current += 1;\n setIsLoading(false);\n }, [cancelTimer, rearmPendingRetry]);\n\n const cacheKeyRef = useRef(cacheKey);\n useIsomorphicLayoutEffect(() => {\n if (cacheKeyRef.current === cacheKey) return;\n cacheKeyRef.current = cacheKey;\n invalidatePending();\n retryableQueryRef.current = null;\n pendingRetryQueryRef.current = null;\n setState({ query: NO_QUERY, items: [], failed: false });\n }, [cacheKey, invalidatePending]);\n\n useEffect(() => {\n if (enabled) return;\n invalidatePending();\n setState((s) =>\n s.query === NO_QUERY ? s : { query: NO_QUERY, items: [], failed: false },\n );\n }, [enabled, invalidatePending]);\n\n // Render-time searches can outlive an abandoned render, so they only arm\n // request work after this hook commits.\n useIsomorphicLayoutEffect(() => {\n inactiveRef.current = false;\n const deferredQuery = deferredQueryRef.current;\n deferredQueryRef.current = null;\n if (deferredQuery !== null) scheduleFetchRef.current(deferredQuery);\n return () => {\n inactiveRef.current = true;\n invalidatePending();\n };\n }, [invalidatePending]);\n\n // Arm retries only after the failed state commits. Arming during rejection\n // would let the failure render immediately schedule another request.\n useEffect(() => {\n retryableQueryRef.current = state.failed ? state.query : null;\n }, [state]);\n\n const adapter = useMemo<Unstable_TriggerAdapter>(\n () => ({\n categories: () => [],\n categoryItems: () => [],\n search: (query: string) => {\n // search() runs inside the popover's render; defer state updates with\n // queueMicrotask so they are not dispatched while another component renders.\n if (query !== state.query || retryableQueryRef.current === query) {\n queueMicrotask(() => scheduleFetch(query));\n } else {\n queueMicrotask(() => {\n if (\n deferredQueryRef.current !== null &&\n deferredQueryRef.current !== query\n ) {\n deferredQueryRef.current = null;\n }\n if (\n pendingQueryRef.current !== null &&\n pendingQueryRef.current !== query\n ) {\n invalidatePending();\n }\n });\n }\n return state.items;\n },\n }),\n [state, scheduleFetch, invalidatePending],\n );\n\n return { adapter, isLoading };\n}\n"],"mappings":"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"}
@@ -1,10 +1,15 @@
1
1
  import { type FC } from "react";
2
2
  import type { Unstable_DirectiveFormatter, Unstable_TriggerAdapter, Unstable_TriggerItem } from "@assistant-ui/core";
3
3
  import type { ReadonlyJSONObject } from "assistant-stream/utils";
4
- /** Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`. */
4
+ /**
5
+ * Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`.
6
+ *
7
+ * @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release.
8
+ */
5
9
  export type Unstable_IconComponent = FC<{
6
10
  className?: string;
7
11
  }>;
12
+ /** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
8
13
  export type Unstable_Mention = {
9
14
  readonly id: string;
10
15
  readonly type: string;
@@ -14,11 +19,13 @@ export type Unstable_Mention = {
14
19
  readonly icon?: string | undefined;
15
20
  readonly metadata?: ReadonlyJSONObject | undefined;
16
21
  };
22
+ /** @deprecated Experimental since 2026-03-16. Not scheduled for removal; the API may change in any release. */
17
23
  export type Unstable_MentionCategory = {
18
24
  readonly id: string;
19
25
  readonly label: string;
20
26
  readonly items: readonly Unstable_Mention[];
21
27
  };
28
+ /** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
22
29
  export type Unstable_ModelContextToolsOptions = {
23
30
  /**
24
31
  * Wrap tools in a dedicated category. Selects drill-down mode on its own
@@ -33,6 +40,7 @@ export type Unstable_ModelContextToolsOptions = {
33
40
  /** Default icon key for each tool. */
34
41
  readonly icon?: string;
35
42
  };
43
+ /** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
36
44
  export type Unstable_UseMentionAdapterOptions = {
37
45
  /**
38
46
  * Flat mention list. Ignored when `categories` is set, and keeps the
@@ -60,17 +68,18 @@ export type Unstable_UseMentionAdapterOptions = {
60
68
  /** Fallback icon when no entry in `iconMap` matches. */
61
69
  readonly fallbackIcon?: Unstable_IconComponent;
62
70
  };
71
+ /** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
63
72
  export type Unstable_MentionDirective = {
64
73
  readonly formatter: Unstable_DirectiveFormatter;
65
74
  readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;
66
75
  };
67
76
  /**
68
- * @deprecated Under active development and might change without notice.
69
- *
70
77
  * Creates a spreadable `{ adapter, directive }` bundle for `@` mentions.
71
78
  * Supports tools registered in model context, explicit items, or both —
72
79
  * flat or categorized.
73
80
  *
81
+ * @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release.
82
+ *
74
83
  * @example
75
84
  * ```tsx
76
85
  * const mention = unstable_useMentionAdapter();
@@ -1 +1 @@
1
- {"version":3,"file":"useMentionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useMentionAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAEzC,OAAO,KAAK,EACV,2BAA2B,EAC3B,uBAAuB,EAEvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAQjE,6EAA6E;AAC7E,MAAM,MAAM,sBAAsB,GAAG,EAAE,CAAC;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEhE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;CACpD,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,oCAAoC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IACpD,sCAAsC;IACtC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC1D;;;;;;;;OAQG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAC9B,OAAO,GACP,iCAAiC,CAAC;IACtC,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,2BAA2B,CAAC;IACjD,yDAAyD;IACzD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC3D,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC1D,wDAAwD;IACxD,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,SAAS,EAAE,2BAA2B,CAAC;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC1E,CAAC;AAsCF;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,iCAAiC,GAC1C;IACD,OAAO,EAAE,uBAAuB,CAAC;IACjC,SAAS,EAAE,yBAAyB,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,sBAAsB,CAAC;CACvC,CAoGA"}
1
+ {"version":3,"file":"useMentionAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useMentionAdapter.ts"],"names":[],"mappings":"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"}
@@ -29,12 +29,12 @@ const toolMentionSource = {
29
29
  isEqual: shallowEqualRecords
30
30
  };
31
31
  /**
32
- * @deprecated Under active development and might change without notice.
33
- *
34
32
  * Creates a spreadable `{ adapter, directive }` bundle for `@` mentions.
35
33
  * Supports tools registered in model context, explicit items, or both —
36
34
  * flat or categorized.
37
35
  *
36
+ * @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release.
37
+ *
38
38
  * @example
39
39
  * ```tsx
40
40
  * const mention = unstable_useMentionAdapter();
@@ -1 +1 @@
1
- {"version":3,"file":"useMentionAdapter.js","names":["useMemo","FC","useAui","Unstable_DirectiveFormatter","Unstable_TriggerAdapter","Unstable_TriggerCategory","Unstable_TriggerItem","unstable_defaultDirectiveFormatter","ReadonlyJSONObject","matchesTriggerItemQuery","shallowEqualRecords","useModelContextSnapshot","ModelContextSnapshotSource","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","EMPTY_TOOL_MENTIONS","Readonly","Object","freeze","toolMentionSource","empty","read","aui","tools","thread","getModelContext","mentions","create","name","tool","entries","subscribe","onChange","unsubscribeContext","on","unsubscribeSelection","scope","event","isEqual","unstable_useMentionAdapter","options","adapter","directive","includeTools","toolsConfig","undefined","wantsTools","isCategorized","toolMentions","defaultIcon","toolItems","map","toTriggerItem","groups","cat","allGroups","length","categoryItems","find","g","search","query","lower","toLowerCase","flatMap","filter","flatItems","seen","Set","i","flatPool","t","has","m"],"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\";\nimport { matchesTriggerItemQuery } from \"../primitives/composer/trigger/matchesTriggerItemQuery\";\nimport {\n shallowEqualRecords,\n useModelContextSnapshot,\n type ModelContextSnapshotSource,\n} from \"./useModelContextSnapshot\";\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 /**\n * Wrap tools in a dedicated category. Selects drill-down mode on its own\n * when `categories` is unset and `items` is omitted.\n */\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 /**\n * Flat mention list. Ignored when `categories` is set, and keeps the\n * adapter flat when a tool `category` is configured.\n */\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; `category` also selects drill-down mode.\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\nconst EMPTY_TOOL_MENTIONS: Readonly<Record<string, string | undefined>> =\n Object.freeze({});\n\n// The description is copied out rather than reached through the tool, so a\n// provider that returns a stable tool object and edits it in place is observed.\nconst toolMentionSource: ModelContextSnapshotSource<\n Readonly<Record<string, string | undefined>>\n> = {\n empty: EMPTY_TOOL_MENTIONS,\n read: (aui) => {\n const tools = aui.thread.getModelContext().tools;\n if (!tools) return EMPTY_TOOL_MENTIONS;\n const mentions = Object.create(null) as Record<string, string | undefined>;\n for (const [name, tool] of Object.entries(tools)) {\n mentions[name] = tool.description;\n }\n return mentions;\n },\n subscribe: (aui, onChange) => {\n const unsubscribeContext = aui.on(\"thread.modelContextUpdate\", onChange);\n // Rebinding the thread event subject to a new thread does not replay it,\n // so a switch between threads carrying different providers is its own\n // refresh trigger. Subscribed globally because a composer can render\n // without a thread list scope.\n const unsubscribeSelection = aui.on(\n { scope: \"*\", event: \"threads.selectionChanged\" },\n onChange,\n );\n return () => {\n unsubscribeContext();\n unsubscribeSelection();\n };\n },\n isEqual: shallowEqualRecords,\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 const isCategorized =\n categories !== undefined ||\n (toolsConfig?.category !== undefined && items === undefined);\n const toolMentions = useModelContextSnapshot(\n aui,\n wantsTools,\n toolMentionSource,\n );\n\n const adapter = useMemo<Unstable_TriggerAdapter>(() => {\n const formatLabel = toolsConfig?.formatLabel;\n const defaultIcon = toolsConfig?.icon;\n const toolItems = wantsTools\n ? Object.entries(toolMentions).map(([name, description]) =>\n toTriggerItem({\n id: name,\n type: \"tool\",\n label: formatLabel ? formatLabel(name) : name,\n description,\n icon: defaultIcon,\n }),\n )\n : [];\n\n // Categorized: drill-down mode\n if (isCategorized) {\n const groups = (categories ?? []).map((cat) => ({\n id: cat.id,\n label: cat.label,\n items: cat.items.map(toTriggerItem),\n }));\n const allGroups =\n toolItems.length > 0\n ? [\n ...groups,\n {\n id: toolsConfig?.category?.id ?? \"tools\",\n label: toolsConfig?.category?.label ?? \"Tools\",\n items: toolItems,\n },\n ]\n : 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) => matchesTriggerItemQuery(item, lower));\n },\n };\n }\n\n // Flat: items + (optionally) tools, all in one search pool\n const flatItems = (items ?? []).map(toTriggerItem);\n // Dedupe by id — explicit items win.\n const seen = new Set(flatItems.map((i) => i.id));\n const flatPool = [\n ...flatItems,\n ...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 flatPool.filter((item) => matchesTriggerItemQuery(item, lower));\n },\n };\n }, [items, categories, wantsTools, toolsConfig, isCategorized, toolMentions]);\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"],"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","matchesTriggerItemQuery","shallowEqualRecords","useModelContextSnapshot","ModelContextSnapshotSource","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","EMPTY_TOOL_MENTIONS","Readonly","Object","freeze","toolMentionSource","empty","read","aui","tools","thread","getModelContext","mentions","create","name","tool","entries","subscribe","onChange","unsubscribeContext","on","unsubscribeSelection","scope","event","isEqual","unstable_useMentionAdapter","options","adapter","directive","includeTools","toolsConfig","undefined","wantsTools","isCategorized","toolMentions","defaultIcon","toolItems","map","toTriggerItem","groups","cat","allGroups","length","categoryItems","find","g","search","query","lower","toLowerCase","flatMap","filter","flatItems","seen","Set","i","flatPool","t","has","m"],"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\";\nimport { matchesTriggerItemQuery } from \"../primitives/composer/trigger/matchesTriggerItemQuery\";\nimport {\n shallowEqualRecords,\n useModelContextSnapshot,\n type ModelContextSnapshotSource,\n} from \"./useModelContextSnapshot\";\n\n/**\n * Icon component shape consumed by `ComposerTriggerPopover`'s `iconMap`.\n *\n * @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release.\n */\nexport type Unstable_IconComponent = FC<{ className?: string }>;\n\n/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */\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\n/** @deprecated Experimental since 2026-03-16. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_MentionCategory = {\n readonly id: string;\n readonly label: string;\n readonly items: readonly Unstable_Mention[];\n};\n\n/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ModelContextToolsOptions = {\n /**\n * Wrap tools in a dedicated category. Selects drill-down mode on its own\n * when `categories` is unset and `items` is omitted.\n */\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\n/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseMentionAdapterOptions = {\n /**\n * Flat mention list. Ignored when `categories` is set, and keeps the\n * adapter flat when a tool `category` is configured.\n */\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; `category` also selects drill-down mode.\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\n/** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_MentionDirective = {\n readonly formatter: Unstable_DirectiveFormatter;\n readonly onInserted?: ((item: Unstable_TriggerItem) => void) | undefined;\n};\n\nconst EMPTY_TOOL_MENTIONS: Readonly<Record<string, string | undefined>> =\n Object.freeze({});\n\n// The description is copied out rather than reached through the tool, so a\n// provider that returns a stable tool object and edits it in place is observed.\nconst toolMentionSource: ModelContextSnapshotSource<\n Readonly<Record<string, string | undefined>>\n> = {\n empty: EMPTY_TOOL_MENTIONS,\n read: (aui) => {\n const tools = aui.thread.getModelContext().tools;\n if (!tools) return EMPTY_TOOL_MENTIONS;\n const mentions = Object.create(null) as Record<string, string | undefined>;\n for (const [name, tool] of Object.entries(tools)) {\n mentions[name] = tool.description;\n }\n return mentions;\n },\n subscribe: (aui, onChange) => {\n const unsubscribeContext = aui.on(\"thread.modelContextUpdate\", onChange);\n // Rebinding the thread event subject to a new thread does not replay it,\n // so a switch between threads carrying different providers is its own\n // refresh trigger. Subscribed globally because a composer can render\n // without a thread list scope.\n const unsubscribeSelection = aui.on(\n { scope: \"*\", event: \"threads.selectionChanged\" },\n onChange,\n );\n return () => {\n unsubscribeContext();\n unsubscribeSelection();\n };\n },\n isEqual: shallowEqualRecords,\n};\n\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 * @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release.\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 const isCategorized =\n categories !== undefined ||\n (toolsConfig?.category !== undefined && items === undefined);\n const toolMentions = useModelContextSnapshot(\n aui,\n wantsTools,\n toolMentionSource,\n );\n\n const adapter = useMemo<Unstable_TriggerAdapter>(() => {\n const formatLabel = toolsConfig?.formatLabel;\n const defaultIcon = toolsConfig?.icon;\n const toolItems = wantsTools\n ? Object.entries(toolMentions).map(([name, description]) =>\n toTriggerItem({\n id: name,\n type: \"tool\",\n label: formatLabel ? formatLabel(name) : name,\n description,\n icon: defaultIcon,\n }),\n )\n : [];\n\n // Categorized: drill-down mode\n if (isCategorized) {\n const groups = (categories ?? []).map((cat) => ({\n id: cat.id,\n label: cat.label,\n items: cat.items.map(toTriggerItem),\n }));\n const allGroups =\n toolItems.length > 0\n ? [\n ...groups,\n {\n id: toolsConfig?.category?.id ?? \"tools\",\n label: toolsConfig?.category?.label ?? \"Tools\",\n items: toolItems,\n },\n ]\n : 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) => matchesTriggerItemQuery(item, lower));\n },\n };\n }\n\n // Flat: items + (optionally) tools, all in one search pool\n const flatItems = (items ?? []).map(toTriggerItem);\n // Dedupe by id — explicit items win.\n const seen = new Set(flatItems.map((i) => i.id));\n const flatPool = [\n ...flatItems,\n ...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 flatPool.filter((item) => matchesTriggerItemQuery(item, lower));\n },\n };\n }, [items, categories, wantsTools, toolsConfig, isCategorized, toolMentions]);\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"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ /** @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release. */
1
2
  export type Unstable_MessageStallDetectionOptions = {
2
3
  /**
3
4
  * Milliseconds of unchanged message content before the message counts as
@@ -6,6 +7,7 @@ export type Unstable_MessageStallDetectionOptions = {
6
7
  */
7
8
  thresholdMs?: number | undefined;
8
9
  };
10
+ /** @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release. */
9
11
  export type Unstable_MessageStallDetection = {
10
12
  /** True while the message is running and its content has not changed for at least `thresholdMs`. */
11
13
  stalled: boolean;
@@ -13,8 +15,6 @@ export type Unstable_MessageStallDetection = {
13
15
  stalledForMs: number;
14
16
  };
15
17
  /**
16
- * @deprecated Under active development and might change without notice.
17
- *
18
18
  * Detects mid-run output stalls on the current message: while the message is
19
19
  * running, watches its text, reasoning, and tool-argument values plus tool-result
20
20
  * availability and reports a stall once they stop changing for `thresholdMs`.
@@ -22,6 +22,8 @@ export type Unstable_MessageStallDetection = {
22
22
  * provider stalls, after the first tokens have already streamed.
23
23
  *
24
24
  * Must be used inside a message scope.
25
+ *
26
+ * @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release.
25
27
  */
26
28
  export declare function unstable_useMessageStallDetection(options?: Unstable_MessageStallDetectionOptions): Unstable_MessageStallDetection;
27
29
  //# sourceMappingURL=useMessageStallDetection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMessageStallDetection.d.ts","sourceRoot":"","sources":["../../src/unstable/useMessageStallDetection.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,qCAAqC,GAAG;IAClD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,oGAAoG;IACpG,OAAO,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,CAAC,EAAE,qCAAqC,GAC9C,8BAA8B,CAkEhC"}
1
+ {"version":3,"file":"useMessageStallDetection.d.ts","sourceRoot":"","sources":["../../src/unstable/useMessageStallDetection.ts"],"names":[],"mappings":"AAMA,+GAA+G;AAC/G,MAAM,MAAM,qCAAqC,GAAG;IAClD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,8BAA8B,GAAG;IAC3C,oGAAoG;IACpG,OAAO,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,CAAC,EAAE,qCAAqC,GAC9C,8BAA8B,CAkEhC"}
@@ -4,8 +4,6 @@ import { useAuiState } from "@assistant-ui/store";
4
4
  import { useShallowSelector } from "@assistant-ui/store/internal";
5
5
  //#region src/unstable/useMessageStallDetection.ts
6
6
  /**
7
- * @deprecated Under active development and might change without notice.
8
- *
9
7
  * Detects mid-run output stalls on the current message: while the message is
10
8
  * running, watches its text, reasoning, and tool-argument values plus tool-result
11
9
  * availability and reports a stall once they stop changing for `thresholdMs`.
@@ -13,6 +11,8 @@ import { useShallowSelector } from "@assistant-ui/store/internal";
13
11
  * provider stalls, after the first tokens have already streamed.
14
12
  *
15
13
  * Must be used inside a message scope.
14
+ *
15
+ * @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release.
16
16
  */
17
17
  function unstable_useMessageStallDetection(options) {
18
18
  const thresholdMs = options?.thresholdMs ?? 2e3;
@@ -1 +1 @@
1
- {"version":3,"file":"useMessageStallDetection.js","names":["useEffect","useRef","useState","useAuiState","useShallowSelector","Unstable_MessageStallDetectionOptions","thresholdMs","Unstable_MessageStallDetection","stalled","stalledForMs","unstable_useMessageStallDetection","options","activity","s","running","message","status","type","values","content","length","part","push","text","argsText","result","undefined","lastActivityRef","setStalled","now","setNow","current","Date","stall","sinceActivity","id","setTimeout","clearTimeout","setInterval","clearInterval","Math","max"],"sources":["../../src/unstable/useMessageStallDetection.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { useShallowSelector } from \"@assistant-ui/store/internal\";\n\nexport type Unstable_MessageStallDetectionOptions = {\n /**\n * Milliseconds of unchanged message content before the message counts as\n * stalled.\n * @default 2000\n */\n thresholdMs?: number | undefined;\n};\n\nexport type Unstable_MessageStallDetection = {\n /** True while the message is running and its content has not changed for at least `thresholdMs`. */\n stalled: boolean;\n /** Milliseconds since the last observed content change. `0` while not stalled. */\n stalledForMs: number;\n};\n\n/**\n * @deprecated Under active development and might change without notice.\n *\n * Detects mid-run output stalls on the current message: while the message is\n * running, watches its text, reasoning, and tool-argument values plus tool-result\n * availability and reports a stall once they stop changing for `thresholdMs`.\n * Useful for re-surfacing a \"still working\" indicator during tool think-time or\n * provider stalls, after the first tokens have already streamed.\n *\n * Must be used inside a message scope.\n */\nexport function unstable_useMessageStallDetection(\n options?: Unstable_MessageStallDetectionOptions,\n): Unstable_MessageStallDetection {\n const thresholdMs = options?.thresholdMs ?? 2000;\n\n const activity = useAuiState(\n useShallowSelector((s) => {\n const running = s.message.status?.type === \"running\";\n if (!running) return [false];\n\n const values: unknown[] = [true, s.message.content.length];\n\n for (const part of s.message.content) {\n if (part.type === \"text\" || part.type === \"reasoning\") {\n values.push(part.type, part.text);\n } else if (part.type === \"tool-call\") {\n values.push(part.type, part.argsText, part.result !== undefined);\n } else {\n values.push(part.type);\n }\n }\n return values;\n }),\n );\n\n const running = activity[0] === true;\n const lastActivityRef = useRef(0);\n const [stalled, setStalled] = useState(false);\n const [now, setNow] = useState(0);\n\n useEffect(() => {\n if (!running) return undefined;\n lastActivityRef.current = Date.now();\n return undefined;\n }, [running, activity]);\n\n useEffect(() => {\n if (!running) {\n setStalled(false);\n return undefined;\n }\n\n const stall = () => {\n setNow(Date.now());\n setStalled(true);\n };\n const sinceActivity = Date.now() - lastActivityRef.current;\n if (sinceActivity >= thresholdMs) {\n stall();\n return undefined;\n }\n\n setStalled(false);\n const id = setTimeout(stall, thresholdMs - sinceActivity);\n return () => clearTimeout(id);\n }, [running, activity, thresholdMs]);\n\n useEffect(() => {\n if (!stalled) return undefined;\n const id = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(id);\n }, [stalled]);\n\n if (!stalled) return { stalled: false, stalledForMs: 0 };\n return {\n stalled: true,\n stalledForMs: Math.max(0, now - lastActivityRef.current),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiCA,SAAgBU,kCACdC,SACgC;CAChC,MAAML,cAAcK,SAASL,eAAe;CAE5C,MAAMM,WAAWT,YACfC,oBAAoBS,MAAM;EAExB,IAAI,EADYA,EAAEE,QAAQC,QAAQC,SAAS,YAC7B,OAAO,CAAC,KAAK;EAE3B,MAAMC,SAAoB,CAAC,MAAML,EAAEE,QAAQI,QAAQC,MAAM;EAEzD,KAAK,MAAMC,QAAQR,EAAEE,QAAQI,SAC3B,IAAIE,KAAKJ,SAAS,UAAUI,KAAKJ,SAAS,aACxCC,OAAOI,KAAKD,KAAKJ,MAAMI,KAAKE,IAAI;OAC3B,IAAIF,KAAKJ,SAAS,aACvBC,OAAOI,KAAKD,KAAKJ,MAAMI,KAAKG,UAAUH,KAAKI,WAAWC,KAAAA,CAAS;OAE/DR,OAAOI,KAAKD,KAAKJ,IAAI;EAGzB,OAAOC;CACT,CAAC,CACH;CAEA,MAAMJ,UAAUF,SAAS,OAAO;CAChC,MAAMe,kBAAkB1B,OAAO,CAAC;CAChC,MAAM,CAACO,SAASoB,cAAc1B,SAAS,KAAK;CAC5C,MAAM,CAAC2B,KAAKC,UAAU5B,SAAS,CAAC;CAEhCF,gBAAgB;EACd,IAAI,CAACc,SAAS,OAAOY,KAAAA;EACrBC,gBAAgBI,UAAUC,KAAKH,IAAI;CAErC,GAAG,CAACf,SAASF,QAAQ,CAAC;CAEtBZ,gBAAgB;EACd,IAAI,CAACc,SAAS;GACZc,WAAW,KAAK;GAChB;EACF;EAEA,MAAMK,cAAc;GAClBH,OAAOE,KAAKH,IAAI,CAAC;GACjBD,WAAW,IAAI;EACjB;EACA,MAAMM,gBAAgBF,KAAKH,IAAI,IAAIF,gBAAgBI;EACnD,IAAIG,iBAAiB5B,aAAa;GAChC2B,MAAM;GACN;EACF;EAEAL,WAAW,KAAK;EAChB,MAAMO,KAAKC,WAAWH,OAAO3B,cAAc4B,aAAa;EACxD,aAAaG,aAAaF,EAAE;CAC9B,GAAG;EAACrB;EAASF;EAAUN;CAAW,CAAC;CAEnCN,gBAAgB;EACd,IAAI,CAACQ,SAAS,OAAOkB,KAAAA;EACrB,MAAMS,KAAKG,kBAAkBR,OAAOE,KAAKH,IAAI,CAAC,GAAG,GAAI;EACrD,aAAaU,cAAcJ,EAAE;CAC/B,GAAG,CAAC3B,OAAO,CAAC;CAEZ,IAAI,CAACA,SAAS,OAAO;EAAEA,SAAS;EAAOC,cAAc;CAAE;CACvD,OAAO;EACLD,SAAS;EACTC,cAAc+B,KAAKC,IAAI,GAAGZ,MAAMF,gBAAgBI,OAAO;CACzD;AACF"}
1
+ {"version":3,"file":"useMessageStallDetection.js","names":["useEffect","useRef","useState","useAuiState","useShallowSelector","Unstable_MessageStallDetectionOptions","thresholdMs","Unstable_MessageStallDetection","stalled","stalledForMs","unstable_useMessageStallDetection","options","activity","s","running","message","status","type","values","content","length","part","push","text","argsText","result","undefined","lastActivityRef","setStalled","now","setNow","current","Date","stall","sinceActivity","id","setTimeout","clearTimeout","setInterval","clearInterval","Math","max"],"sources":["../../src/unstable/useMessageStallDetection.ts"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { useShallowSelector } from \"@assistant-ui/store/internal\";\n\n/** @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_MessageStallDetectionOptions = {\n /**\n * Milliseconds of unchanged message content before the message counts as\n * stalled.\n * @default 2000\n */\n thresholdMs?: number | undefined;\n};\n\n/** @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_MessageStallDetection = {\n /** True while the message is running and its content has not changed for at least `thresholdMs`. */\n stalled: boolean;\n /** Milliseconds since the last observed content change. `0` while not stalled. */\n stalledForMs: number;\n};\n\n/**\n * Detects mid-run output stalls on the current message: while the message is\n * running, watches its text, reasoning, and tool-argument values plus tool-result\n * availability and reports a stall once they stop changing for `thresholdMs`.\n * Useful for re-surfacing a \"still working\" indicator during tool think-time or\n * provider stalls, after the first tokens have already streamed.\n *\n * Must be used inside a message scope.\n *\n * @deprecated Experimental since 2026-06-13. Not scheduled for removal; the API may change in any release.\n */\nexport function unstable_useMessageStallDetection(\n options?: Unstable_MessageStallDetectionOptions,\n): Unstable_MessageStallDetection {\n const thresholdMs = options?.thresholdMs ?? 2000;\n\n const activity = useAuiState(\n useShallowSelector((s) => {\n const running = s.message.status?.type === \"running\";\n if (!running) return [false];\n\n const values: unknown[] = [true, s.message.content.length];\n\n for (const part of s.message.content) {\n if (part.type === \"text\" || part.type === \"reasoning\") {\n values.push(part.type, part.text);\n } else if (part.type === \"tool-call\") {\n values.push(part.type, part.argsText, part.result !== undefined);\n } else {\n values.push(part.type);\n }\n }\n return values;\n }),\n );\n\n const running = activity[0] === true;\n const lastActivityRef = useRef(0);\n const [stalled, setStalled] = useState(false);\n const [now, setNow] = useState(0);\n\n useEffect(() => {\n if (!running) return undefined;\n lastActivityRef.current = Date.now();\n return undefined;\n }, [running, activity]);\n\n useEffect(() => {\n if (!running) {\n setStalled(false);\n return undefined;\n }\n\n const stall = () => {\n setNow(Date.now());\n setStalled(true);\n };\n const sinceActivity = Date.now() - lastActivityRef.current;\n if (sinceActivity >= thresholdMs) {\n stall();\n return undefined;\n }\n\n setStalled(false);\n const id = setTimeout(stall, thresholdMs - sinceActivity);\n return () => clearTimeout(id);\n }, [running, activity, thresholdMs]);\n\n useEffect(() => {\n if (!stalled) return undefined;\n const id = setInterval(() => setNow(Date.now()), 1000);\n return () => clearInterval(id);\n }, [stalled]);\n\n if (!stalled) return { stalled: false, stalledForMs: 0 };\n return {\n stalled: true,\n stalledForMs: Math.max(0, now - lastActivityRef.current),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmCA,SAAgBU,kCACdC,SACgC;CAChC,MAAML,cAAcK,SAASL,eAAe;CAE5C,MAAMM,WAAWT,YACfC,oBAAoBS,MAAM;EAExB,IAAI,EADYA,EAAEE,QAAQC,QAAQC,SAAS,YAC7B,OAAO,CAAC,KAAK;EAE3B,MAAMC,SAAoB,CAAC,MAAML,EAAEE,QAAQI,QAAQC,MAAM;EAEzD,KAAK,MAAMC,QAAQR,EAAEE,QAAQI,SAC3B,IAAIE,KAAKJ,SAAS,UAAUI,KAAKJ,SAAS,aACxCC,OAAOI,KAAKD,KAAKJ,MAAMI,KAAKE,IAAI;OAC3B,IAAIF,KAAKJ,SAAS,aACvBC,OAAOI,KAAKD,KAAKJ,MAAMI,KAAKG,UAAUH,KAAKI,WAAWC,KAAAA,CAAS;OAE/DR,OAAOI,KAAKD,KAAKJ,IAAI;EAGzB,OAAOC;CACT,CAAC,CACH;CAEA,MAAMJ,UAAUF,SAAS,OAAO;CAChC,MAAMe,kBAAkB1B,OAAO,CAAC;CAChC,MAAM,CAACO,SAASoB,cAAc1B,SAAS,KAAK;CAC5C,MAAM,CAAC2B,KAAKC,UAAU5B,SAAS,CAAC;CAEhCF,gBAAgB;EACd,IAAI,CAACc,SAAS,OAAOY,KAAAA;EACrBC,gBAAgBI,UAAUC,KAAKH,IAAI;CAErC,GAAG,CAACf,SAASF,QAAQ,CAAC;CAEtBZ,gBAAgB;EACd,IAAI,CAACc,SAAS;GACZc,WAAW,KAAK;GAChB;EACF;EAEA,MAAMK,cAAc;GAClBH,OAAOE,KAAKH,IAAI,CAAC;GACjBD,WAAW,IAAI;EACjB;EACA,MAAMM,gBAAgBF,KAAKH,IAAI,IAAIF,gBAAgBI;EACnD,IAAIG,iBAAiB5B,aAAa;GAChC2B,MAAM;GACN;EACF;EAEAL,WAAW,KAAK;EAChB,MAAMO,KAAKC,WAAWH,OAAO3B,cAAc4B,aAAa;EACxD,aAAaG,aAAaF,EAAE;CAC9B,GAAG;EAACrB;EAASF;EAAUN;CAAW,CAAC;CAEnCN,gBAAgB;EACd,IAAI,CAACQ,SAAS,OAAOkB,KAAAA;EACrB,MAAMS,KAAKG,kBAAkBR,OAAOE,KAAKH,IAAI,CAAC,GAAG,GAAI;EACrD,aAAaU,cAAcJ,EAAE;CAC/B,GAAG,CAAC3B,OAAO,CAAC;CAEZ,IAAI,CAACA,SAAS,OAAO;EAAEA,SAAS;EAAOC,cAAc;CAAE;CACvD,OAAO;EACLD,SAAS;EACTC,cAAc+B,KAAKC,IAAI,GAAGZ,MAAMF,gBAAgBI,OAAO;CACzD;AACF"}
@@ -1,5 +1,6 @@
1
1
  import type { Unstable_TriggerAdapter, Unstable_TriggerItem } from "@assistant-ui/core";
2
2
  import type { Unstable_IconComponent } from "./useMentionAdapter.js";
3
+ /** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
3
4
  export type Unstable_SlashCommand = {
4
5
  readonly id: string;
5
6
  readonly label?: string | undefined;
@@ -7,6 +8,7 @@ export type Unstable_SlashCommand = {
7
8
  readonly icon?: string | undefined;
8
9
  readonly execute: () => void;
9
10
  };
11
+ /** @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release. */
10
12
  export type Unstable_UseSlashCommandAdapterOptions = {
11
13
  readonly commands: readonly Unstable_SlashCommand[];
12
14
  /** Strip the trigger text from the composer after executing. @default false */
@@ -16,18 +18,19 @@ export type Unstable_UseSlashCommandAdapterOptions = {
16
18
  /** Fallback icon when no entry in `iconMap` matches. */
17
19
  readonly fallbackIcon?: Unstable_IconComponent;
18
20
  };
21
+ /** @deprecated Experimental since 2026-04-15. Not scheduled for removal; the API may change in any release. */
19
22
  export type Unstable_SlashCommandAction = {
20
23
  readonly onExecute: (item: Unstable_TriggerItem) => void;
21
24
  readonly removeOnExecute?: boolean | undefined;
22
25
  };
23
26
  /**
24
- * @deprecated Under active development and may change without notice.
25
- *
26
27
  * Bundles slash command definitions (with inline `execute` callbacks) into
27
28
  * `{adapter, action}` that plug directly into `ComposerTriggerPopover`.
28
29
  * `execute` stays in the hook closure and is never attached to the returned
29
30
  * `TriggerItem`, keeping items serializable.
30
31
  *
32
+ * @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release.
33
+ *
31
34
  * @example
32
35
  * ```tsx
33
36
  * const slash = unstable_useSlashCommandAdapter({
@@ -1 +1 @@
1
- {"version":3,"file":"useSlashCommandAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useSlashCommandAdapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,+BAA4B;AAGlE,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,sCAAsC,GAAG;IACnD,QAAQ,CAAC,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACpD,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/C,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC1D,wDAAwD;IACxD,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACzD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C;IACD,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,sBAAsB,CAAC;CACvC,CAiDA"}
1
+ {"version":3,"file":"useSlashCommandAdapter.d.ts","sourceRoot":"","sources":["../../src/unstable/useSlashCommandAdapter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,sBAAsB,EAAE,+BAA4B;AAGlE,+GAA+G;AAC/G,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,sCAAsC,GAAG;IACnD,QAAQ,CAAC,QAAQ,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACpD,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/C,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAC1D,wDAAwD;IACxD,QAAQ,CAAC,YAAY,CAAC,EAAE,sBAAsB,CAAC;CAChD,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACzD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C;IACD,OAAO,EAAE,uBAAuB,CAAC;IACjC,MAAM,EAAE,2BAA2B,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,sBAAsB,CAAC;CACvC,CAiDA"}
@@ -1,15 +1,16 @@
1
1
  "use client";
2
+ import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect.js";
2
3
  import { matchesTriggerItemQuery } from "../primitives/composer/trigger/matchesTriggerItemQuery.js";
3
- import { useLayoutEffect, useMemo, useRef } from "@assistant-ui/tap/react-shim";
4
+ import { useMemo, useRef } from "@assistant-ui/tap/react-shim";
4
5
  //#region src/unstable/useSlashCommandAdapter.ts
5
6
  /**
6
- * @deprecated Under active development and may change without notice.
7
- *
8
7
  * Bundles slash command definitions (with inline `execute` callbacks) into
9
8
  * `{adapter, action}` that plug directly into `ComposerTriggerPopover`.
10
9
  * `execute` stays in the hook closure and is never attached to the returned
11
10
  * `TriggerItem`, keeping items serializable.
12
11
  *
12
+ * @deprecated Experimental since 2026-04-06. Not scheduled for removal; the API may change in any release.
13
+ *
13
14
  * @example
14
15
  * ```tsx
15
16
  * const slash = unstable_useSlashCommandAdapter({
@@ -28,7 +29,7 @@ function unstable_useSlashCommandAdapter(options) {
28
29
  const committedItemsRef = useRef(void 0);
29
30
  const nextItems = commands.map(toItem);
30
31
  const items = areTriggerItemsEqual(committedItemsRef.current, nextItems) ? committedItemsRef.current : nextItems;
31
- useLayoutEffect(() => {
32
+ useIsomorphicLayoutEffect(() => {
32
33
  commandsRef.current = commands;
33
34
  committedItemsRef.current = items;
34
35
  }, [commands, items]);