@assistant-ui/react 0.15.28 → 0.15.30

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 (71) hide show
  1. package/dist/adapters/OpenAIRealtimeVoiceAdapter.d.ts +22 -0
  2. package/dist/adapters/OpenAIRealtimeVoiceAdapter.d.ts.map +1 -0
  3. package/dist/adapters/OpenAIRealtimeVoiceAdapter.js +297 -0
  4. package/dist/adapters/OpenAIRealtimeVoiceAdapter.js.map +1 -0
  5. package/dist/context/stores/ThreadViewport.d.ts +8 -0
  6. package/dist/context/stores/ThreadViewport.d.ts.map +1 -1
  7. package/dist/context/stores/ThreadViewport.js +24 -8
  8. package/dist/context/stores/ThreadViewport.js.map +1 -1
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +3 -2
  12. package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js +1 -1
  13. package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  14. package/dist/mcp-apps/McpAppRenderer.js +5 -1
  15. package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
  16. package/dist/model-context/frame/useAssistantFrameHost.d.ts.map +1 -1
  17. package/dist/model-context/frame/useAssistantFrameHost.js +67 -46
  18. package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
  19. package/dist/model-context/makeAssistantVisible.js +1 -1
  20. package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
  21. package/dist/primitives/composer/ComposerAddAttachment.js +28 -10
  22. package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
  23. package/dist/primitives/composer/ComposerInput.d.ts +4 -1
  24. package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
  25. package/dist/primitives/composer/ComposerInput.js +15 -2
  26. package/dist/primitives/composer/ComposerInput.js.map +1 -1
  27. package/dist/primitives/composer/ComposerResume.d.ts +23 -0
  28. package/dist/primitives/composer/ComposerResume.d.ts.map +1 -0
  29. package/dist/primitives/composer/ComposerResume.js +34 -0
  30. package/dist/primitives/composer/ComposerResume.js.map +1 -0
  31. package/dist/primitives/composer/trigger/TriggerPopoverAction.js +1 -1
  32. package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +1 -1
  33. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts +6 -2
  34. package/dist/primitives/composer/trigger/triggerSelectionResource.d.ts.map +1 -1
  35. package/dist/primitives/composer/trigger/triggerSelectionResource.js +4 -1
  36. package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
  37. package/dist/primitives/composer.d.ts +1 -0
  38. package/dist/primitives/composer.d.ts.map +1 -1
  39. package/dist/primitives/composer.js +3 -1
  40. package/dist/primitives/composer.js.map +1 -1
  41. package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
  42. package/dist/primitives/thread/useThreadViewportAutoScroll.js +32 -292
  43. package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
  44. package/dist/unstable/useMentionAdapter.js +1 -1
  45. package/dist/utils/Primitive.d.ts +14 -14
  46. package/dist/utils/hooks/useOnResizeContent.js +1 -1
  47. package/package.json +5 -5
  48. package/src/adapters/OpenAIRealtimeVoiceAdapter.test.ts +748 -0
  49. package/src/adapters/OpenAIRealtimeVoiceAdapter.ts +388 -0
  50. package/src/context/stores/ThreadViewport.ts +36 -8
  51. package/src/index.ts +6 -0
  52. package/src/mcp-apps/McpAppRenderer.test.tsx +65 -0
  53. package/src/mcp-apps/McpAppRenderer.tsx +13 -2
  54. package/src/model-context/frame/useAssistantFrameHost.detach.test.tsx +145 -0
  55. package/src/model-context/frame/useAssistantFrameHost.reload.test.tsx +106 -0
  56. package/src/model-context/frame/useAssistantFrameHost.test.tsx +3 -1
  57. package/src/model-context/frame/useAssistantFrameHost.ts +72 -24
  58. package/src/model-context/makeAssistantVisible.test.tsx +25 -37
  59. package/src/primitives/composer/ComposerAddAttachment.test.tsx +1 -0
  60. package/src/primitives/composer/ComposerAddAttachment.thread-scope.test.tsx +202 -0
  61. package/src/primitives/composer/ComposerAddAttachment.ts +11 -1
  62. package/src/primitives/composer/ComposerInput.focus-on-run-start.test.tsx +130 -0
  63. package/src/primitives/composer/ComposerInput.tsx +30 -4
  64. package/src/primitives/composer/ComposerResume.ts +36 -0
  65. package/src/primitives/composer/trigger/triggerSelectionResource.test.ts +24 -0
  66. package/src/primitives/composer/trigger/triggerSelectionResource.ts +14 -3
  67. package/src/primitives/composer/useAuiState.composer.test.tsx +84 -0
  68. package/src/primitives/composer.ts +1 -0
  69. package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +90 -0
  70. package/src/primitives/thread/useThreadViewportAutoScroll.ts +48 -486
  71. package/src/model-context/frame/AssistantFrame.test.ts +0 -400
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { OpenAIRealtimeVoiceAdapter } from "./adapters/OpenAIRealtimeVoiceAdapter.js";
1
2
  import { useAssistantTransportRuntime, useAssistantTransportSendCommand, useAssistantTransportState } from "./assistant-transport.js";
2
3
  import { useThreadViewport, useThreadViewportStore } from "./context/react/ThreadViewportContext.js";
3
4
  import { DevToolsHooks, DevToolsProviderApi } from "./devtools/DevToolsHooks.js";
@@ -64,9 +65,9 @@ import { McpAppRenderer } from "./mcp-apps/McpAppRenderer.js";
64
65
  import { McpAppsRemoteHost } from "./mcp-apps/McpAppsRemoteHost.js";
65
66
  import { defaultWebMcpFilter } from "./unstable/webmcp/convertTools.js";
66
67
  import { unstable_useWebMcpProvider } from "./unstable/webmcp/useWebMcpProvider.js";
68
+ import { AssistantFrameHost, AssistantFrameProvider, CompositeAttachmentAdapter, ExportedMessageRepository, FRAME_MESSAGE_CHANNEL, InMemoryThreadListAdapter, MessageNotSentError, ModelContextRegistry, SimpleImageAttachmentAdapter, SimpleTextAttachmentAdapter, WebSpeechDictationAdapter, WebSpeechSynthesisAdapter, bindExternalStoreMessage, createMessageQueue, createSuggestionAdapter, createVoiceSession, defaultDirectiveFormatter, fromThreadMessageLike, generateId, getExternalStoreMessages, isMessageNotSentError, mergeModelContexts, pickExternalStoreSharedOptions, tool, toolApprovalAcceptsText, unstable_defaultDirectiveFormatter, unstable_formatInteractableSnapshot, unstable_getInteractableSnapshots, unstable_getInteractableVersions } from "@assistant-ui/core";
67
69
  import { ChainOfThoughtPartByIndexProvider, DataRenderers, GenerativeUIRender, GenerativeUIRenderError, Interactables, RuntimeAdapterProvider, Tools, createSimpleTitleAdapter, createThreadRowsSelector, defineMcpToolkit, defineToolkit, externalTool, groupPartByType, hitl, hitlTool, humanTool, makeAssistantDataUI, makeAssistantTool, makeAssistantToolUI, providerTool, stubTool, unstable_Interactables, unstable_interactableTool, unstable_useInteractable, unstable_useInteractableState, unstable_useInteractableVersions, unstable_useThreadMessageIds, useAssistantContext, useAssistantDataUI, useAssistantInstructions, useAssistantInteractable, useAssistantTool, useAssistantToolUI, useAuiToolOverrides, useCloudThreadListRuntime, useExternalStoreSharedOptions, useInlineRender, useInteractableState, useRuntimeAdapters, useToolArgsStatus, useVoiceControls, useVoiceState, useVoiceVolume } from "@assistant-ui/core/react";
68
70
  import { AuiConfig, AuiIf, AuiProvider, useAui, useAuiEvent, useAuiState } from "@assistant-ui/store";
69
71
  import { ChainOfThoughtClient, ExternalThread, InMemoryThreadList, ModelContext as ModelContextClient, RemoteThreadList, SingleThreadList, Suggestions } from "@assistant-ui/core/store";
70
- import { AssistantFrameHost, AssistantFrameProvider, CompositeAttachmentAdapter, ExportedMessageRepository, FRAME_MESSAGE_CHANNEL, InMemoryThreadListAdapter, MessageNotSentError, ModelContextRegistry, SimpleImageAttachmentAdapter, SimpleTextAttachmentAdapter, WebSpeechDictationAdapter, WebSpeechSynthesisAdapter, bindExternalStoreMessage, createMessageQueue, createSuggestionAdapter, createVoiceSession, defaultDirectiveFormatter, fromThreadMessageLike, generateId, getExternalStoreMessages, isMessageNotSentError, mergeModelContexts, pickExternalStoreSharedOptions, tool, toolApprovalAcceptsText, unstable_defaultDirectiveFormatter, unstable_formatInteractableSnapshot, unstable_getInteractableSnapshots, unstable_getInteractableVersions } from "@assistant-ui/core";
71
72
  import { AssistantCloud, readAnonymousRefreshToken } from "assistant-cloud";
72
- export { actionBarMore_exports as ActionBarMorePrimitive, actionBar_exports as ActionBarPrimitive, AssistantCloud, AssistantFrameHost, AssistantFrameProvider, assistantModal_exports as AssistantModalPrimitive, AssistantRuntimeProvider, attachment_exports as AttachmentPrimitive, AuiConfig, AuiIf, AuiProvider, branchPicker_exports as BranchPickerPrimitive, ChainOfThoughtByIndicesProvider, ChainOfThoughtClient, ChainOfThoughtPartByIndexProvider, chainOfThought_exports as ChainOfThoughtPrimitive, CloudFileAttachmentAdapter, CloudRendererHost, ComposerAttachmentByIndexProvider, composer_exports as ComposerPrimitive, CompositeAttachmentAdapter, DataRenderers, DevToolsHooks, DevToolsProviderApi, error_exports as ErrorPrimitive, ExportedMessageRepository, ExternalThread, FRAME_MESSAGE_CHANNEL, GenerativeUIRender, GenerativeUIRenderError, internal_exports as INTERNAL, InMemoryThreadList, InMemoryThreadListAdapter, Interactables, McpAppRenderer, McpAppsRemoteHost, MessageAttachmentByIndexProvider, MessageByIndexProvider, MessageNotSentError, messagePart_exports as MessagePartPrimitive, message_exports as MessagePrimitive, MessageProvider, ModelContextClient, ModelContextRegistry, PartByIndexProvider, queueItem_exports as QueueItemPrimitive, ReadonlyThreadProvider, RemoteThreadList, RuntimeAdapterProvider, selectionToolbar_exports as SelectionToolbarPrimitive, SimpleImageAttachmentAdapter, SimpleTextAttachmentAdapter, SingleThreadList, SuggestionByIndexProvider, suggestion_exports as SuggestionPrimitive, Suggestions, TextMessagePartProvider, ThreadListItemByIndexProvider, threadListItemMore_exports as ThreadListItemMorePrimitive, threadListItem_exports as ThreadListItemPrimitive, ThreadListItemRuntimeProvider, threadList_exports as ThreadListPrimitive, thread_exports as ThreadPrimitive, Tools, WebSpeechDictationAdapter, WebSpeechSynthesisAdapter, bindExternalStoreMessage, createMessageQueue, createSimpleTitleAdapter, createSuggestionAdapter, createThreadRowsSelector, createVoiceSession, defaultDirectiveFormatter, defineMcpToolkit, defineToolkit, externalTool, fromThreadMessageLike, generateId, getExternalStoreMessages, getMcpAppFromToolPart, groupPartByType, hitl, hitlTool, humanTool, isMessageNotSentError, makeAssistantDataUI, makeAssistantTool, makeAssistantToolUI, makeAssistantVisible, mergeModelContexts, pickExternalStoreSharedOptions, providerTool, readAnonymousRefreshToken, stubTool, tool, toolApprovalAcceptsText, unstable_Interactables, convertExternalMessages as unstable_convertExternalMessages, createExternalMessageConversionCache as unstable_createExternalMessageConversionCache, createMessageConverter as unstable_createMessageConverter, unstable_defaultDirectiveFormatter, defaultWebMcpFilter as unstable_defaultWebMcpFilter, unstable_formatInteractableSnapshot, unstable_getInteractableSnapshots, unstable_getInteractableVersions, unstable_interactableTool, unstable_useComposerInput, unstable_useComposerInputHistory, unstable_useInteractable, unstable_useInteractableState, unstable_useInteractableVersions, unstable_useLiveCompletionAdapter, unstable_useMentionAdapter, unstable_useMessageStallDetection, unstable_useSlashCommandAdapter, unstable_useThreadMessageIds, unstable_useTriggerPopoverAriaProps, useTriggerPopoverRootContext as unstable_useTriggerPopoverRootContext, useTriggerPopoverRootContextOptional as unstable_useTriggerPopoverRootContextOptional, useTriggerPopoverScopeContext as unstable_useTriggerPopoverScopeContext, useTriggerPopoverScopeContextOptional as unstable_useTriggerPopoverScopeContextOptional, useTriggerPopoverTriggers as unstable_useTriggerPopoverTriggers, useTriggerPopoverTriggersOptional as unstable_useTriggerPopoverTriggersOptional, unstable_useWebMcpProvider, useAssistantContext, useAssistantDataUI, useAssistantFrameHost, useAssistantInstructions, useAssistantInteractable, useAssistantTool, useAssistantToolUI, useAssistantTransportRuntime, useAssistantTransportSendCommand, useAssistantTransportState, useAui, useAuiEvent, useAuiState, useAuiToolOverrides, useCloudThreadListAdapter, useCloudThreadListRuntime, useExternalMessageConverter, useExternalStoreRuntime, useExternalStoreSharedOptions, useInlineRender, useInteractableState, useLocalRuntime, useMessagePartData, useMessagePartFile, useMessagePartImage, useMessagePartReasoning, useMessagePartSource, useMessagePartText, useMessageQuote, useMessageTiming, useRemoteThreadListRuntime, useRuntimeAdapters, useScrollLock, useSmooth, useThreadViewport, useThreadViewportAutoScroll, useThreadViewportStore, useToolArgsStatus, useToolCallElapsed, useVoiceControls, useVoiceState, useVoiceVolume };
73
+ export { actionBarMore_exports as ActionBarMorePrimitive, actionBar_exports as ActionBarPrimitive, AssistantCloud, AssistantFrameHost, AssistantFrameProvider, assistantModal_exports as AssistantModalPrimitive, AssistantRuntimeProvider, attachment_exports as AttachmentPrimitive, AuiConfig, AuiIf, AuiProvider, branchPicker_exports as BranchPickerPrimitive, ChainOfThoughtByIndicesProvider, ChainOfThoughtClient, ChainOfThoughtPartByIndexProvider, chainOfThought_exports as ChainOfThoughtPrimitive, CloudFileAttachmentAdapter, CloudRendererHost, ComposerAttachmentByIndexProvider, composer_exports as ComposerPrimitive, CompositeAttachmentAdapter, DataRenderers, DevToolsHooks, DevToolsProviderApi, error_exports as ErrorPrimitive, ExportedMessageRepository, ExternalThread, FRAME_MESSAGE_CHANNEL, GenerativeUIRender, GenerativeUIRenderError, internal_exports as INTERNAL, InMemoryThreadList, InMemoryThreadListAdapter, Interactables, McpAppRenderer, McpAppsRemoteHost, MessageAttachmentByIndexProvider, MessageByIndexProvider, MessageNotSentError, messagePart_exports as MessagePartPrimitive, message_exports as MessagePrimitive, MessageProvider, ModelContextClient, ModelContextRegistry, OpenAIRealtimeVoiceAdapter, PartByIndexProvider, queueItem_exports as QueueItemPrimitive, ReadonlyThreadProvider, RemoteThreadList, RuntimeAdapterProvider, selectionToolbar_exports as SelectionToolbarPrimitive, SimpleImageAttachmentAdapter, SimpleTextAttachmentAdapter, SingleThreadList, SuggestionByIndexProvider, suggestion_exports as SuggestionPrimitive, Suggestions, TextMessagePartProvider, ThreadListItemByIndexProvider, threadListItemMore_exports as ThreadListItemMorePrimitive, threadListItem_exports as ThreadListItemPrimitive, ThreadListItemRuntimeProvider, threadList_exports as ThreadListPrimitive, thread_exports as ThreadPrimitive, Tools, WebSpeechDictationAdapter, WebSpeechSynthesisAdapter, bindExternalStoreMessage, createMessageQueue, createSimpleTitleAdapter, createSuggestionAdapter, createThreadRowsSelector, createVoiceSession, defaultDirectiveFormatter, defineMcpToolkit, defineToolkit, externalTool, fromThreadMessageLike, generateId, getExternalStoreMessages, getMcpAppFromToolPart, groupPartByType, hitl, hitlTool, humanTool, isMessageNotSentError, makeAssistantDataUI, makeAssistantTool, makeAssistantToolUI, makeAssistantVisible, mergeModelContexts, pickExternalStoreSharedOptions, providerTool, readAnonymousRefreshToken, stubTool, tool, toolApprovalAcceptsText, unstable_Interactables, convertExternalMessages as unstable_convertExternalMessages, createExternalMessageConversionCache as unstable_createExternalMessageConversionCache, createMessageConverter as unstable_createMessageConverter, unstable_defaultDirectiveFormatter, defaultWebMcpFilter as unstable_defaultWebMcpFilter, unstable_formatInteractableSnapshot, unstable_getInteractableSnapshots, unstable_getInteractableVersions, unstable_interactableTool, unstable_useComposerInput, unstable_useComposerInputHistory, unstable_useInteractable, unstable_useInteractableState, unstable_useInteractableVersions, unstable_useLiveCompletionAdapter, unstable_useMentionAdapter, unstable_useMessageStallDetection, unstable_useSlashCommandAdapter, unstable_useThreadMessageIds, unstable_useTriggerPopoverAriaProps, useTriggerPopoverRootContext as unstable_useTriggerPopoverRootContext, useTriggerPopoverRootContextOptional as unstable_useTriggerPopoverRootContextOptional, useTriggerPopoverScopeContext as unstable_useTriggerPopoverScopeContext, useTriggerPopoverScopeContextOptional as unstable_useTriggerPopoverScopeContextOptional, useTriggerPopoverTriggers as unstable_useTriggerPopoverTriggers, useTriggerPopoverTriggersOptional as unstable_useTriggerPopoverTriggersOptional, unstable_useWebMcpProvider, useAssistantContext, useAssistantDataUI, useAssistantFrameHost, useAssistantInstructions, useAssistantInteractable, useAssistantTool, useAssistantToolUI, useAssistantTransportRuntime, useAssistantTransportSendCommand, useAssistantTransportState, useAui, useAuiEvent, useAuiState, useAuiToolOverrides, useCloudThreadListAdapter, useCloudThreadListRuntime, useExternalMessageConverter, useExternalStoreRuntime, useExternalStoreSharedOptions, useInlineRender, useInteractableState, useLocalRuntime, useMessagePartData, useMessagePartFile, useMessagePartImage, useMessagePartReasoning, useMessagePartSource, useMessagePartText, useMessageQuote, useMessageTiming, useRemoteThreadListRuntime, useRuntimeAdapters, useScrollLock, useSmooth, useThreadViewport, useThreadViewportAutoScroll, useThreadViewportStore, useToolArgsStatus, useToolCallElapsed, useVoiceControls, useVoiceState, useVoiceVolume };
@@ -1,3 +1,3 @@
1
- import { MessageRepository } from "@assistant-ui/core/internal";
2
1
  import { ExportedMessageRepository } from "@assistant-ui/core";
2
+ import { MessageRepository } from "@assistant-ui/core/internal";
3
3
  export { ExportedMessageRepository, MessageRepository };
@@ -1 +1 @@
1
- {"version":3,"file":"McpAppRenderer.d.ts","sourceRoot":"","sources":["../../src/mcp-apps/McpAppRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7E,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEhF,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EAEd,mBAAmB,EACnB,WAAW,EACZ,mBAAgB;AAIjB;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uEAAuE;IACvE,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,2GAA2G;IAC3G,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,IAAI,CACb,oBAAoB,EACpB,UAAU,GAAG,cAAc,GAAG,eAAe,CAC9C,CAAC;IACF,wGAAwG;IACxG,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,sEAAsE;IACtE,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,uEAAuE;IACvE,aAAa,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACtD;;;;OAIG;IACH,IAAI,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACnC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,iBAAiB,CAAC;CAC5D,CAAC;AA8RF,eAAO,MAAM,cAAc;qBA3BL,4BAA4B;oCA2BO,CAAC"}
1
+ {"version":3,"file":"McpAppRenderer.d.ts","sourceRoot":"","sources":["../../src/mcp-apps/McpAppRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE7E,OAAO,KAAK,EAGV,mBAAmB,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,4BAA4B,EAE7B,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEhF,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EAEd,mBAAmB,EACnB,WAAW,EACZ,mBAAgB;AAIjB;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uEAAuE;IACvE,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,2GAA2G;IAC3G,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,IAAI,CACb,oBAAoB,EACpB,UAAU,GAAG,cAAc,GAAG,eAAe,CAC9C,CAAC;IACF,wGAAwG;IACxG,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,sEAAsE;IACtE,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,uEAAuE;IACvE,aAAa,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACtD;;;;OAIG;IACH,IAAI,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC;IACnC;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,iBAAiB,CAAC;CAC5D,CAAC;AAySF,eAAO,MAAM,cAAc;qBA3BL,4BAA4B;oCA2BO,CAAC"}
@@ -31,7 +31,11 @@ function extractSendMessageTexts(params) {
31
31
  if (typeof obj["prompt"] === "string") return [obj["prompt"]];
32
32
  if (typeof obj["text"] === "string") return [obj["text"]];
33
33
  if (typeof obj["message"] === "string") return [obj["message"]];
34
- if (Array.isArray(obj["content"])) return obj["content"].filter((block) => isRecord(block) && block["type"] === "text" && typeof block["text"] === "string").map((block) => block.text);
34
+ const content = obj["content"];
35
+ if (Array.isArray(content)) return Object.keys(content).filter((key) => {
36
+ const index = Number(key);
37
+ return Number.isInteger(index) && index >= 0 && index < content.length && String(index) === key;
38
+ }).map((key) => content[Number(key)]).filter((block) => isRecord(block) && block["type"] === "text" && typeof block["text"] === "string").map((block) => block.text);
35
39
  }
36
40
  function extractSendMessageContent(params) {
37
41
  const texts = extractSendMessageTexts(params);
@@ -1 +1 @@
1
- {"version":3,"file":"McpAppRenderer.js","names":["useEffect","useMemo","useRef","useState","ReactNode","useIsomorphicLayoutEffect","McpAppMetadata","TextMessagePart","ToolCallMessagePart","ToolCallMessagePartComponent","ToolCallMessagePartProps","useAui","create","StoreApi","UseBoundStore","useResource","resource","ResourceElement","McpAppFrame","McpAppBridgeHandlers","McpAppHostContext","McpAppHostInfo","McpAppResource","McpAppSandboxConfig","McpAppsHost","getMcpAppFromToolPart","isRecord","McpAppPartOptions","sandbox","maxHeight","hostInfo","hostContext","handlers","Omit","fallback","loadingFallback","errorFallback","error","Error","McpAppRendererOptions","host","forPart","part","LoadedResourceState","resourceUri","serverId","RendererState","options","UseRendererStore","isSameOptions","a","b","aKeys","Object","keys","filter","key","bKeys","length","aValues","Record","bValues","every","hasOwn","is","getInput","status","type","argsText","args","undefined","defaultOpenLink","url","window","open","extractSendMessageTexts","params","obj","Array","isArray","block","text","map","extractSendMessageContent","content","reason","texts","resolvePartOptions","overrides","InlineRenderer","useRendererStore","aui","rendererOptions","state","opts","app","cachedAppRef","current","appForRender","loadedResource","setLoadedResource","callerHandlers","cancelled","targetHost","targetUri","targetServerId","loadResource","Promise","resolve","getState","res","uri","String","bridgeHandlers","openLink","sendMessage","result","isError","ok","thread","append","callTool","readResource","listResources","loadedResourceForApp","appResource","useMcpAppRenderer","render","setState","prev","Render","props","displayName","McpAppRenderer"],"sources":["../../src/mcp-apps/McpAppRenderer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport type {\n McpAppMetadata,\n TextMessagePart,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type {\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n} from \"@assistant-ui/core/react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { create, type StoreApi, type UseBoundStore } from \"zustand\";\n\nimport { useResource, resource, type ResourceElement } from \"@assistant-ui/tap\";\nimport { McpAppFrame } from \"./app-frame\";\nimport type {\n McpAppBridgeHandlers,\n McpAppHostContext,\n McpAppHostInfo,\n McpAppResource,\n McpAppSandboxConfig,\n McpAppsHost,\n} from \"./types\";\nimport { getMcpAppFromToolPart } from \"./utils\";\nimport { isRecord } from \"@assistant-ui/core/internal\";\n\n/**\n * Options that apply to a single MCP app. `McpAppRenderer` takes them as\n * thread-wide defaults, and its `forPart` resolver overrides them for one part.\n */\nexport type McpAppPartOptions = {\n /** Sandbox + container styling. Passes through to SafeContentFrame. */\n sandbox?: McpAppSandboxConfig;\n /**\n * Upper bound (in pixels) applied to the widget-driven auto-resize height.\n * Defaults to 800.\n */\n maxHeight?: number;\n /** Identifies the host to the widget in the `ui/initialize` response. */\n hostInfo?: McpAppHostInfo;\n /** Delivered to the widget on initialize and pushed via `notifications/host_context/changed` on change. */\n hostContext?: McpAppHostContext;\n /**\n * Optional widget interaction and lifecycle handlers. Data-plane handlers\n * (`callTool`, `readResource`, `listResources`) always use `host`.\n *\n * Which handlers a widget may call is captured when its frame mounts, so a\n * `forPart` resolver returns the same handler keys for a part on every\n * render even when the implementations change.\n */\n handlers?: Omit<\n McpAppBridgeHandlers,\n \"callTool\" | \"readResource\" | \"listResources\"\n >;\n /** Rendered when no MCP app is on the part, or while load is in flight / failed (unless overridden). */\n fallback?: ReactNode;\n /** Rendered while the resource is loading. Defaults to `fallback`. */\n loadingFallback?: ReactNode;\n /** Rendered when the resource load rejects. Defaults to `fallback`. */\n errorFallback?: ReactNode | ((error: Error) => ReactNode);\n};\n\nexport type McpAppRendererOptions = McpAppPartOptions & {\n /**\n * Provides the data-plane operations the widget can request\n * (`loadResource`, `callTool`, `readResource`, `listResources`). Use\n * `McpAppsRemoteHost({ url })` for the default HTTP-route convention.\n */\n host: ResourceElement<McpAppsHost>;\n /**\n * Resolves the options for one part. Each returned value replaces the\n * thread-wide option of the same name, so a host can give every MCP app its\n * own display mode and answer `requestDisplayMode` for the part that asked.\n *\n * `handlers` is the exception and merges per key. Its keys are capabilities\n * negotiated with the widget rather than data, so replacing the whole bag to\n * add one handler would withdraw the others from that part's widget with\n * nothing to observe it by.\n */\n forPart?: (part: ToolCallMessagePart) => McpAppPartOptions;\n};\n\ntype LoadedResourceState = {\n host: McpAppsHost;\n resourceUri: string;\n serverId?: string;\n resource?: McpAppResource;\n error?: Error;\n};\n\ntype RendererState = {\n host: McpAppsHost;\n options: McpAppRendererOptions;\n};\n\ntype UseRendererStore = UseBoundStore<StoreApi<RendererState>>;\n\n// Callers pass options as an object literal, so the resource hands this a fresh\n// identity on every run; comparing the values keeps an unchanged run from\n// re-rendering every mounted part. `host` is excluded because it is an element\n// rebuilt on every run, and the store carries the resolved host separately.\nconst isSameOptions = (\n a: McpAppRendererOptions,\n b: McpAppRendererOptions,\n): boolean => {\n const aKeys = Object.keys(a).filter((key) => key !== \"host\");\n const bKeys = Object.keys(b).filter((key) => key !== \"host\");\n if (aKeys.length !== bKeys.length) return false;\n const aValues = a as Record<string, unknown>;\n const bValues = b as Record<string, unknown>;\n return aKeys.every(\n (key) =>\n Object.hasOwn(bValues, key) && Object.is(aValues[key], bValues[key]),\n );\n};\n\nfunction getInput(part: {\n status: { type: string };\n argsText: string;\n args: unknown;\n}): unknown {\n if (\n part.status.type === \"running\" &&\n (part.argsText === \"\" || part.argsText === \"{}\")\n ) {\n return undefined;\n }\n return part.args;\n}\n\nconst defaultOpenLink = ({ url }: { url: string }) => {\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n};\n\nfunction extractSendMessageTexts(params: unknown): string[] | undefined {\n if (typeof params === \"string\") return [params];\n if (!params || typeof params !== \"object\") return undefined;\n const obj = params as Record<string, unknown>;\n if (typeof obj[\"prompt\"] === \"string\") return [obj[\"prompt\"]];\n if (typeof obj[\"text\"] === \"string\") return [obj[\"text\"]];\n if (typeof obj[\"message\"] === \"string\") return [obj[\"message\"]];\n if (Array.isArray(obj[\"content\"])) {\n return obj[\"content\"]\n .filter(\n (block): block is { type: \"text\"; text: string } =>\n isRecord(block) &&\n block[\"type\"] === \"text\" &&\n typeof block[\"text\"] === \"string\",\n )\n .map((block) => block.text);\n }\n return undefined;\n}\n\nfunction extractSendMessageContent(\n params: unknown,\n): { content: TextMessagePart[] } | { reason: string } {\n const texts = extractSendMessageTexts(params);\n if (!texts) return { reason: \"unrecognised params shape\" };\n const content = texts\n .filter((text) => text !== \"\")\n .map((text): TextMessagePart => ({ type: \"text\", text }));\n if (content.length === 0) {\n return { reason: \"no text content to append\" };\n }\n return { content };\n}\n\nfunction resolvePartOptions(\n options: McpAppRendererOptions,\n part: ToolCallMessagePartProps,\n): McpAppRendererOptions {\n const overrides = options.forPart?.(part);\n if (!overrides) return options;\n const handlers =\n options.handlers && overrides.handlers\n ? { ...options.handlers, ...overrides.handlers }\n : (overrides.handlers ?? options.handlers);\n return {\n ...options,\n ...overrides,\n ...(handlers === undefined ? {} : { handlers }),\n };\n}\n\nfunction InlineRenderer({\n part,\n useRendererStore,\n}: {\n part: ToolCallMessagePartProps;\n useRendererStore: UseRendererStore;\n}) {\n const aui = useAui();\n const rendererOptions = useRendererStore((state) => state.options);\n const opts = resolvePartOptions(rendererOptions, part);\n const app = getMcpAppFromToolPart(part);\n const cachedAppRef = useRef<McpAppMetadata | undefined>(undefined);\n useIsomorphicLayoutEffect(() => {\n if (app != null) cachedAppRef.current = app;\n }, [app]);\n const appForRender = app ?? cachedAppRef.current;\n\n const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();\n\n const host = useRendererStore((state) => state.host);\n const resourceUri = appForRender?.resourceUri;\n const serverId = appForRender?.serverId;\n const callerHandlers = opts.handlers;\n useEffect(() => {\n if (resourceUri == null) return;\n let cancelled = false;\n const targetHost = host;\n const targetUri = resourceUri;\n const targetServerId = serverId;\n\n // Host changes are published later in this passive flush. Defer until that\n // publication lands, then verify this effect still owns the current host.\n const loadResource = async () => {\n await Promise.resolve();\n if (cancelled || useRendererStore.getState().host !== targetHost) return;\n try {\n const res = await targetHost.loadResource({\n uri: targetUri,\n ...(targetServerId ? { serverId: targetServerId } : {}),\n });\n if (!cancelled)\n setLoadedResource({\n host: targetHost,\n resourceUri: targetUri,\n ...(targetServerId !== undefined\n ? { serverId: targetServerId }\n : {}),\n resource: res,\n });\n } catch (error: unknown) {\n if (!cancelled) {\n setLoadedResource({\n host: targetHost,\n resourceUri: targetUri,\n ...(targetServerId !== undefined\n ? { serverId: targetServerId }\n : {}),\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n };\n void loadResource();\n\n return () => {\n cancelled = true;\n };\n }, [host, resourceUri, serverId, useRendererStore]);\n\n const bridgeHandlers = useMemo<McpAppBridgeHandlers>(\n () => ({\n ...callerHandlers,\n openLink: callerHandlers?.openLink ?? defaultOpenLink,\n sendMessage:\n callerHandlers?.sendMessage ??\n ((params) => {\n const result = extractSendMessageContent(params);\n if (\"reason\" in result) {\n return { isError: true, ok: false, reason: result.reason };\n }\n aui.thread.append({ content: result.content });\n return { ok: true };\n }),\n callTool: (params) =>\n useRendererStore.getState().host.callTool({\n ...params,\n ...(serverId ? { serverId } : {}),\n }),\n readResource: (params) =>\n useRendererStore.getState().host.readResource({\n ...params,\n ...(serverId ? { serverId } : {}),\n }),\n listResources: (params) => {\n if (!serverId) {\n return useRendererStore.getState().host.listResources(params);\n }\n return useRendererStore.getState().host.listResources({\n ...(isRecord(params) ? params : {}),\n serverId,\n });\n },\n }),\n [aui, callerHandlers, serverId, useRendererStore],\n );\n\n const loadedResourceForApp =\n loadedResource?.host === host &&\n loadedResource?.resourceUri === appForRender?.resourceUri &&\n loadedResource?.serverId === appForRender?.serverId\n ? loadedResource\n : undefined;\n const appResource = loadedResourceForApp?.resource;\n const error = loadedResourceForApp?.error;\n\n const fallback = opts.fallback ?? null;\n if (appForRender == null) {\n return <>{fallback}</>;\n }\n if (error != null) {\n const errorFallback = opts.errorFallback;\n if (errorFallback === undefined) return <>{fallback}</>;\n if (typeof errorFallback === \"function\") return <>{errorFallback(error)}</>;\n return <>{errorFallback}</>;\n }\n if (appResource == null) {\n return <>{opts.loadingFallback ?? fallback}</>;\n }\n\n return (\n <McpAppFrame\n app={appForRender}\n resource={appResource}\n input={getInput(part)}\n output={part.result}\n sandbox={opts.sandbox}\n handlers={bridgeHandlers}\n hostInfo={opts.hostInfo}\n hostContext={opts.hostContext}\n maxHeight={opts.maxHeight}\n />\n );\n}\n\n/**\n * Creates a tool-call renderer for MCP Apps embedded in assistant messages.\n *\n * Compose this into the `Tools` resource through its `mcpApp` option. When a\n * tool-call part carries `mcp.app` metadata for a `ui://` resource, the\n * renderer loads that resource from the configured host and displays it in a\n * sandboxed frame.\n */\nconst useMcpAppRenderer = (\n options: McpAppRendererOptions,\n): { readonly render: ToolCallMessagePartComponent } => {\n const host = useResource(options.host);\n\n // The rendered component identity has to stay stable, or every part remounts\n // its frame; options reach the parts through the store instead of props.\n const [useRendererStore] = useState(() =>\n create<RendererState>(() => ({ host, options })),\n );\n useEffect(() => {\n useRendererStore.setState((prev) =>\n prev.host === host && isSameOptions(prev.options, options)\n ? prev\n : { host, options },\n );\n }, [host, options, useRendererStore]);\n\n const render = useMemo((): ToolCallMessagePartComponent => {\n const Render: ToolCallMessagePartComponent = (props) => (\n <InlineRenderer part={props} useRendererStore={useRendererStore} />\n );\n Render.displayName = \"McpAppRenderer\";\n return Render;\n }, [useRendererStore]);\n\n return { render };\n};\n\nexport const McpAppRenderer = resource(useMcpAppRenderer);\n"],"mappings":"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"}
1
+ {"version":3,"file":"McpAppRenderer.js","names":["useEffect","useMemo","useRef","useState","ReactNode","useIsomorphicLayoutEffect","McpAppMetadata","TextMessagePart","ToolCallMessagePart","ToolCallMessagePartComponent","ToolCallMessagePartProps","useAui","create","StoreApi","UseBoundStore","useResource","resource","ResourceElement","McpAppFrame","McpAppBridgeHandlers","McpAppHostContext","McpAppHostInfo","McpAppResource","McpAppSandboxConfig","McpAppsHost","getMcpAppFromToolPart","isRecord","McpAppPartOptions","sandbox","maxHeight","hostInfo","hostContext","handlers","Omit","fallback","loadingFallback","errorFallback","error","Error","McpAppRendererOptions","host","forPart","part","LoadedResourceState","resourceUri","serverId","RendererState","options","UseRendererStore","isSameOptions","a","b","aKeys","Object","keys","filter","key","bKeys","length","aValues","Record","bValues","every","hasOwn","is","getInput","status","type","argsText","args","undefined","defaultOpenLink","url","window","open","extractSendMessageTexts","params","obj","content","Array","isArray","index","Number","isInteger","String","map","block","text","extractSendMessageContent","reason","texts","resolvePartOptions","overrides","InlineRenderer","useRendererStore","aui","rendererOptions","state","opts","app","cachedAppRef","current","appForRender","loadedResource","setLoadedResource","callerHandlers","cancelled","targetHost","targetUri","targetServerId","loadResource","Promise","resolve","getState","res","uri","bridgeHandlers","openLink","sendMessage","result","isError","ok","thread","append","callTool","readResource","listResources","loadedResourceForApp","appResource","useMcpAppRenderer","render","setState","prev","Render","props","displayName","McpAppRenderer"],"sources":["../../src/mcp-apps/McpAppRenderer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../utils/useIsomorphicLayoutEffect\";\nimport type {\n McpAppMetadata,\n TextMessagePart,\n ToolCallMessagePart,\n} from \"@assistant-ui/core\";\nimport type {\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n} from \"@assistant-ui/core/react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { create, type StoreApi, type UseBoundStore } from \"zustand\";\n\nimport { useResource, resource, type ResourceElement } from \"@assistant-ui/tap\";\nimport { McpAppFrame } from \"./app-frame\";\nimport type {\n McpAppBridgeHandlers,\n McpAppHostContext,\n McpAppHostInfo,\n McpAppResource,\n McpAppSandboxConfig,\n McpAppsHost,\n} from \"./types\";\nimport { getMcpAppFromToolPart } from \"./utils\";\nimport { isRecord } from \"@assistant-ui/core/internal\";\n\n/**\n * Options that apply to a single MCP app. `McpAppRenderer` takes them as\n * thread-wide defaults, and its `forPart` resolver overrides them for one part.\n */\nexport type McpAppPartOptions = {\n /** Sandbox + container styling. Passes through to SafeContentFrame. */\n sandbox?: McpAppSandboxConfig;\n /**\n * Upper bound (in pixels) applied to the widget-driven auto-resize height.\n * Defaults to 800.\n */\n maxHeight?: number;\n /** Identifies the host to the widget in the `ui/initialize` response. */\n hostInfo?: McpAppHostInfo;\n /** Delivered to the widget on initialize and pushed via `notifications/host_context/changed` on change. */\n hostContext?: McpAppHostContext;\n /**\n * Optional widget interaction and lifecycle handlers. Data-plane handlers\n * (`callTool`, `readResource`, `listResources`) always use `host`.\n *\n * Which handlers a widget may call is captured when its frame mounts, so a\n * `forPart` resolver returns the same handler keys for a part on every\n * render even when the implementations change.\n */\n handlers?: Omit<\n McpAppBridgeHandlers,\n \"callTool\" | \"readResource\" | \"listResources\"\n >;\n /** Rendered when no MCP app is on the part, or while load is in flight / failed (unless overridden). */\n fallback?: ReactNode;\n /** Rendered while the resource is loading. Defaults to `fallback`. */\n loadingFallback?: ReactNode;\n /** Rendered when the resource load rejects. Defaults to `fallback`. */\n errorFallback?: ReactNode | ((error: Error) => ReactNode);\n};\n\nexport type McpAppRendererOptions = McpAppPartOptions & {\n /**\n * Provides the data-plane operations the widget can request\n * (`loadResource`, `callTool`, `readResource`, `listResources`). Use\n * `McpAppsRemoteHost({ url })` for the default HTTP-route convention.\n */\n host: ResourceElement<McpAppsHost>;\n /**\n * Resolves the options for one part. Each returned value replaces the\n * thread-wide option of the same name, so a host can give every MCP app its\n * own display mode and answer `requestDisplayMode` for the part that asked.\n *\n * `handlers` is the exception and merges per key. Its keys are capabilities\n * negotiated with the widget rather than data, so replacing the whole bag to\n * add one handler would withdraw the others from that part's widget with\n * nothing to observe it by.\n */\n forPart?: (part: ToolCallMessagePart) => McpAppPartOptions;\n};\n\ntype LoadedResourceState = {\n host: McpAppsHost;\n resourceUri: string;\n serverId?: string;\n resource?: McpAppResource;\n error?: Error;\n};\n\ntype RendererState = {\n host: McpAppsHost;\n options: McpAppRendererOptions;\n};\n\ntype UseRendererStore = UseBoundStore<StoreApi<RendererState>>;\n\n// Callers pass options as an object literal, so the resource hands this a fresh\n// identity on every run; comparing the values keeps an unchanged run from\n// re-rendering every mounted part. `host` is excluded because it is an element\n// rebuilt on every run, and the store carries the resolved host separately.\nconst isSameOptions = (\n a: McpAppRendererOptions,\n b: McpAppRendererOptions,\n): boolean => {\n const aKeys = Object.keys(a).filter((key) => key !== \"host\");\n const bKeys = Object.keys(b).filter((key) => key !== \"host\");\n if (aKeys.length !== bKeys.length) return false;\n const aValues = a as Record<string, unknown>;\n const bValues = b as Record<string, unknown>;\n return aKeys.every(\n (key) =>\n Object.hasOwn(bValues, key) && Object.is(aValues[key], bValues[key]),\n );\n};\n\nfunction getInput(part: {\n status: { type: string };\n argsText: string;\n args: unknown;\n}): unknown {\n if (\n part.status.type === \"running\" &&\n (part.argsText === \"\" || part.argsText === \"{}\")\n ) {\n return undefined;\n }\n return part.args;\n}\n\nconst defaultOpenLink = ({ url }: { url: string }) => {\n window.open(url, \"_blank\", \"noopener,noreferrer\");\n};\n\nfunction extractSendMessageTexts(params: unknown): string[] | undefined {\n if (typeof params === \"string\") return [params];\n if (!params || typeof params !== \"object\") return undefined;\n const obj = params as Record<string, unknown>;\n if (typeof obj[\"prompt\"] === \"string\") return [obj[\"prompt\"]];\n if (typeof obj[\"text\"] === \"string\") return [obj[\"text\"]];\n if (typeof obj[\"message\"] === \"string\") return [obj[\"message\"]];\n const content = obj[\"content\"];\n if (Array.isArray(content)) {\n return Object.keys(content)\n .filter((key) => {\n const index = Number(key);\n return (\n Number.isInteger(index) &&\n index >= 0 &&\n index < content.length &&\n String(index) === key\n );\n })\n .map((key) => content[Number(key)])\n .filter(\n (block): block is { type: \"text\"; text: string } =>\n isRecord(block) &&\n block[\"type\"] === \"text\" &&\n typeof block[\"text\"] === \"string\",\n )\n .map((block) => block.text);\n }\n return undefined;\n}\n\nfunction extractSendMessageContent(\n params: unknown,\n): { content: TextMessagePart[] } | { reason: string } {\n const texts = extractSendMessageTexts(params);\n if (!texts) return { reason: \"unrecognised params shape\" };\n const content = texts\n .filter((text) => text !== \"\")\n .map((text): TextMessagePart => ({ type: \"text\", text }));\n if (content.length === 0) {\n return { reason: \"no text content to append\" };\n }\n return { content };\n}\n\nfunction resolvePartOptions(\n options: McpAppRendererOptions,\n part: ToolCallMessagePartProps,\n): McpAppRendererOptions {\n const overrides = options.forPart?.(part);\n if (!overrides) return options;\n const handlers =\n options.handlers && overrides.handlers\n ? { ...options.handlers, ...overrides.handlers }\n : (overrides.handlers ?? options.handlers);\n return {\n ...options,\n ...overrides,\n ...(handlers === undefined ? {} : { handlers }),\n };\n}\n\nfunction InlineRenderer({\n part,\n useRendererStore,\n}: {\n part: ToolCallMessagePartProps;\n useRendererStore: UseRendererStore;\n}) {\n const aui = useAui();\n const rendererOptions = useRendererStore((state) => state.options);\n const opts = resolvePartOptions(rendererOptions, part);\n const app = getMcpAppFromToolPart(part);\n const cachedAppRef = useRef<McpAppMetadata | undefined>(undefined);\n useIsomorphicLayoutEffect(() => {\n if (app != null) cachedAppRef.current = app;\n }, [app]);\n const appForRender = app ?? cachedAppRef.current;\n\n const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();\n\n const host = useRendererStore((state) => state.host);\n const resourceUri = appForRender?.resourceUri;\n const serverId = appForRender?.serverId;\n const callerHandlers = opts.handlers;\n useEffect(() => {\n if (resourceUri == null) return;\n let cancelled = false;\n const targetHost = host;\n const targetUri = resourceUri;\n const targetServerId = serverId;\n\n // Host changes are published later in this passive flush. Defer until that\n // publication lands, then verify this effect still owns the current host.\n const loadResource = async () => {\n await Promise.resolve();\n if (cancelled || useRendererStore.getState().host !== targetHost) return;\n try {\n const res = await targetHost.loadResource({\n uri: targetUri,\n ...(targetServerId ? { serverId: targetServerId } : {}),\n });\n if (!cancelled)\n setLoadedResource({\n host: targetHost,\n resourceUri: targetUri,\n ...(targetServerId !== undefined\n ? { serverId: targetServerId }\n : {}),\n resource: res,\n });\n } catch (error: unknown) {\n if (!cancelled) {\n setLoadedResource({\n host: targetHost,\n resourceUri: targetUri,\n ...(targetServerId !== undefined\n ? { serverId: targetServerId }\n : {}),\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n };\n void loadResource();\n\n return () => {\n cancelled = true;\n };\n }, [host, resourceUri, serverId, useRendererStore]);\n\n const bridgeHandlers = useMemo<McpAppBridgeHandlers>(\n () => ({\n ...callerHandlers,\n openLink: callerHandlers?.openLink ?? defaultOpenLink,\n sendMessage:\n callerHandlers?.sendMessage ??\n ((params) => {\n const result = extractSendMessageContent(params);\n if (\"reason\" in result) {\n return { isError: true, ok: false, reason: result.reason };\n }\n aui.thread.append({ content: result.content });\n return { ok: true };\n }),\n callTool: (params) =>\n useRendererStore.getState().host.callTool({\n ...params,\n ...(serverId ? { serverId } : {}),\n }),\n readResource: (params) =>\n useRendererStore.getState().host.readResource({\n ...params,\n ...(serverId ? { serverId } : {}),\n }),\n listResources: (params) => {\n if (!serverId) {\n return useRendererStore.getState().host.listResources(params);\n }\n return useRendererStore.getState().host.listResources({\n ...(isRecord(params) ? params : {}),\n serverId,\n });\n },\n }),\n [aui, callerHandlers, serverId, useRendererStore],\n );\n\n const loadedResourceForApp =\n loadedResource?.host === host &&\n loadedResource?.resourceUri === appForRender?.resourceUri &&\n loadedResource?.serverId === appForRender?.serverId\n ? loadedResource\n : undefined;\n const appResource = loadedResourceForApp?.resource;\n const error = loadedResourceForApp?.error;\n\n const fallback = opts.fallback ?? null;\n if (appForRender == null) {\n return <>{fallback}</>;\n }\n if (error != null) {\n const errorFallback = opts.errorFallback;\n if (errorFallback === undefined) return <>{fallback}</>;\n if (typeof errorFallback === \"function\") return <>{errorFallback(error)}</>;\n return <>{errorFallback}</>;\n }\n if (appResource == null) {\n return <>{opts.loadingFallback ?? fallback}</>;\n }\n\n return (\n <McpAppFrame\n app={appForRender}\n resource={appResource}\n input={getInput(part)}\n output={part.result}\n sandbox={opts.sandbox}\n handlers={bridgeHandlers}\n hostInfo={opts.hostInfo}\n hostContext={opts.hostContext}\n maxHeight={opts.maxHeight}\n />\n );\n}\n\n/**\n * Creates a tool-call renderer for MCP Apps embedded in assistant messages.\n *\n * Compose this into the `Tools` resource through its `mcpApp` option. When a\n * tool-call part carries `mcp.app` metadata for a `ui://` resource, the\n * renderer loads that resource from the configured host and displays it in a\n * sandboxed frame.\n */\nconst useMcpAppRenderer = (\n options: McpAppRendererOptions,\n): { readonly render: ToolCallMessagePartComponent } => {\n const host = useResource(options.host);\n\n // The rendered component identity has to stay stable, or every part remounts\n // its frame; options reach the parts through the store instead of props.\n const [useRendererStore] = useState(() =>\n create<RendererState>(() => ({ host, options })),\n );\n useEffect(() => {\n useRendererStore.setState((prev) =>\n prev.host === host && isSameOptions(prev.options, options)\n ? prev\n : { host, options },\n );\n }, [host, options, useRendererStore]);\n\n const render = useMemo((): ToolCallMessagePartComponent => {\n const Render: ToolCallMessagePartComponent = (props) => (\n <InlineRenderer part={props} useRendererStore={useRendererStore} />\n );\n Render.displayName = \"McpAppRenderer\";\n return Render;\n }, [useRendererStore]);\n\n return { render };\n};\n\nexport const McpAppRenderer = resource(useMcpAppRenderer);\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAssistantFrameHost.d.ts","sourceRoot":"","sources":["../../../src/model-context/frame/useAssistantFrameHost.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,KAAK,4BAA4B,GAAG;IAClC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,SAAS,EAAE,kBAAkB,KAAK,WAAW,CAAC;CAC1D,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,qBAAqB,GAAI,wCAInC,4BAA4B,KAAG,IAqCjC,CAAC"}
1
+ {"version":3,"file":"useAssistantFrameHost.d.ts","sourceRoot":"","sources":["../../../src/model-context/frame/useAssistantFrameHost.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,KAAK,4BAA4B,GAAG;IAClC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC;IACrE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,SAAS,EAAE,kBAAkB,KAAK,WAAW,CAAC;CAC1D,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,qBAAqB,GAAI,wCAInC,4BAA4B,KAAG,IAqFjC,CAAC"}
@@ -1,7 +1,6 @@
1
1
  "use client";
2
- import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
- import { useEffect } from "@assistant-ui/tap/react-shim";
4
2
  import { AssistantFrameHost } from "@assistant-ui/core";
3
+ import { useEffect } from "@assistant-ui/tap/react-shim";
5
4
  //#region src/model-context/frame/useAssistantFrameHost.ts
6
5
  /**
7
6
  * React hook that manages the lifecycle of an AssistantFrameHost and its binding to the current AssistantRuntime.
@@ -20,55 +19,77 @@ import { AssistantFrameHost } from "@assistant-ui/core";
20
19
  * }
21
20
  * ```
22
21
  */
23
- const useAssistantFrameHost = (t0) => {
24
- const $ = c(5);
25
- const { iframeRef, targetOrigin, register } = t0;
26
- let t1;
27
- let t2;
28
- if ($[0] !== iframeRef || $[1] !== register || $[2] !== targetOrigin) {
29
- t1 = () => {
30
- const iframeWindow = iframeRef.current?.contentWindow;
31
- if (!iframeWindow) return;
32
- const frameHost = new AssistantFrameHost(iframeWindow, targetOrigin);
33
- const unsubscribe = register(frameHost);
34
- return () => {
35
- let cleanupFailed = false;
36
- let cleanupError;
22
+ const useAssistantFrameHost = ({ iframeRef, targetOrigin, register }) => {
23
+ useEffect(() => {
24
+ let active;
25
+ const syncFrame = () => {
26
+ const iframe = iframeRef.current;
27
+ const iframeWindow = iframe?.isConnected === false ? null : iframe?.contentWindow;
28
+ if (active && active.iframe === iframe && active.window === iframeWindow) return;
29
+ const previous = active;
30
+ active = void 0;
31
+ let disposalFailed = false;
32
+ let disposalError;
33
+ try {
34
+ previous?.dispose();
35
+ } catch (error) {
36
+ disposalFailed = true;
37
+ disposalError = error;
38
+ }
39
+ if (iframe && iframeWindow) {
40
+ const frameHost = new AssistantFrameHost(iframeWindow, targetOrigin);
41
+ let unsubscribe;
37
42
  try {
43
+ unsubscribe = register(frameHost);
44
+ } catch (error_0) {
38
45
  frameHost.dispose();
39
- } catch (t3) {
40
- const error = t3;
41
- cleanupFailed = true;
42
- cleanupError = error;
46
+ throw error_0;
43
47
  }
44
- try {
45
- unsubscribe();
46
- } catch (t4) {
47
- const error_0 = t4;
48
- if (cleanupFailed) console.error("[assistant-ui] AssistantFrameHost unregistration failed.", error_0);
49
- else {
50
- cleanupFailed = true;
51
- cleanupError = error_0;
48
+ const onLoad = () => frameHost.frameLoaded();
49
+ iframe.addEventListener("load", onLoad);
50
+ active = {
51
+ iframe,
52
+ window: iframeWindow,
53
+ dispose: () => {
54
+ iframe.removeEventListener("load", onLoad);
55
+ let cleanupFailed = false;
56
+ let cleanupError;
57
+ try {
58
+ frameHost.dispose();
59
+ } catch (error_1) {
60
+ cleanupFailed = true;
61
+ cleanupError = error_1;
62
+ }
63
+ try {
64
+ unsubscribe();
65
+ } catch (error_2) {
66
+ if (cleanupFailed) console.error("[assistant-ui] AssistantFrameHost unregistration failed.", error_2);
67
+ else {
68
+ cleanupFailed = true;
69
+ cleanupError = error_2;
70
+ }
71
+ }
72
+ if (cleanupFailed) throw cleanupError;
52
73
  }
53
- }
54
- if (cleanupFailed) throw cleanupError;
55
- };
74
+ };
75
+ }
76
+ if (disposalFailed) throw disposalError;
77
+ };
78
+ syncFrame();
79
+ const observer = new MutationObserver(syncFrame);
80
+ observer.observe(document, {
81
+ childList: true,
82
+ subtree: true
83
+ });
84
+ return () => {
85
+ observer.disconnect();
86
+ active?.dispose();
56
87
  };
57
- t2 = [
58
- iframeRef,
59
- targetOrigin,
60
- register
61
- ];
62
- $[0] = iframeRef;
63
- $[1] = register;
64
- $[2] = targetOrigin;
65
- $[3] = t1;
66
- $[4] = t2;
67
- } else {
68
- t1 = $[3];
69
- t2 = $[4];
70
- }
71
- useEffect(t1, t2);
88
+ }, [
89
+ iframeRef,
90
+ targetOrigin,
91
+ register
92
+ ]);
72
93
  };
73
94
  //#endregion
74
95
  export { useAssistantFrameHost };
@@ -1 +1 @@
1
- {"version":3,"file":"useAssistantFrameHost.js","names":["c","_c","Unsubscribe","useEffect","RefObject","AssistantFrameHost","UseAssistantFrameHostOptions","iframeRef","Readonly","HTMLIFrameElement","targetOrigin","register","frameHost","useAssistantFrameHost","t0","$","t1","t2","iframeWindow","current","contentWindow","unsubscribe","cleanupFailed","cleanupError","dispose","t3","error","t4","console","_c"],"sources":["../../../src/model-context/frame/useAssistantFrameHost.ts"],"sourcesContent":["\"use client\";\n\nimport type { Unsubscribe } from \"@assistant-ui/core\";\nimport { useEffect, type RefObject } from \"react\";\nimport { AssistantFrameHost } from \"@assistant-ui/core\";\n\ntype UseAssistantFrameHostOptions = {\n iframeRef: Readonly<RefObject<HTMLIFrameElement | null | undefined>>;\n targetOrigin?: string;\n register: (frameHost: AssistantFrameHost) => Unsubscribe;\n};\n\n/**\n * React hook that manages the lifecycle of an AssistantFrameHost and its binding to the current AssistantRuntime.\n *\n * Usage example:\n * ```typescript\n * function MyComponent() {\n * const iframeRef = useRef<HTMLIFrameElement>(null);\n *\n * useAssistantFrameHost({\n * iframeRef,\n * targetOrigin: \"https://trusted-domain.com\",\n * });\n *\n * return <iframe ref={iframeRef} src=\"...\" />;\n * }\n * ```\n */\nexport const useAssistantFrameHost = ({\n iframeRef,\n targetOrigin,\n register,\n}: UseAssistantFrameHostOptions): void => {\n useEffect(() => {\n const iframeWindow = iframeRef.current?.contentWindow;\n if (!iframeWindow) return;\n\n const frameHost = new AssistantFrameHost(iframeWindow, targetOrigin);\n\n const unsubscribe = register(frameHost);\n\n return () => {\n let cleanupFailed = false;\n let cleanupError: unknown;\n\n try {\n frameHost.dispose();\n } catch (error) {\n cleanupFailed = true;\n cleanupError = error;\n }\n\n try {\n unsubscribe();\n } catch (error) {\n if (cleanupFailed) {\n console.error(\n \"[assistant-ui] AssistantFrameHost unregistration failed.\",\n error,\n );\n } else {\n cleanupFailed = true;\n cleanupError = error;\n }\n }\n\n if (cleanupFailed) throw cleanupError;\n };\n }, [iframeRef, targetOrigin, register]);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAaa,yBAAwBC,OAAA;CAAA,MAAAC,IAAAd,EAAA,CAAA;CAAC,MAAA,EAAAM,WAAAG,cAAAC,aAAAG;CAIP,IAAAE;CAAA,IAAAC;CAAA,IAAAF,EAAA,OAAAR,aAAAQ,EAAA,OAAAJ,YAAAI,EAAA,OAAAL,cAAA;EACnBM,WAAA;GACR,MAAAE,eAAqBX,UAASY,SAAuBC;GACrD,IAAI,CAACF,cAAY;GAEjB,MAAAN,YAAkB,IAAIP,mBAAmBa,cAAcR,YAAY;GAEnE,MAAAW,cAAoBV,SAASC,SAAS;GAAE,aAEjC;IACL,IAAAU,gBAAoB;IAChBC,IAAAA;IAEJ,IAAA;KACEX,UAASY,QAAS;IAAC,SAAAC,IAAA;KACZC,MAAAA,QAAAA;KACPJ,gBAAgBA;KAChBC,eAAeG;IAAH;IAGd,IAAA;KACEL,YAAY;IAAC,SAAAM,IAAA;KACND,MAAAA,UAAAA;KACP,IAAIJ,eACFM,QAAOF,MACL,4DACAA,OACF;UAAC;MAEDJ,gBAAgBA;MAChBC,eAAeG;KAAH;IACb;IAGH,IAAIJ,eAAe,MAAMC;GAAa;EACvC;EACAN,KAAA;GAACV;GAAWG;GAAcC;EAAQ;EAACI,EAAA,KAAAR;EAAAQ,EAAA,KAAAJ;EAAAI,EAAA,KAAAL;EAAAK,EAAA,KAAAC;EAAAD,EAAA,KAAAE;CAAA,OAAA;EAAAD,KAAAD,EAAA;EAAAE,KAAAF,EAAA;CAAA;CAnCtCZ,UAAUa,IAmCPC,EAAmC;AAAC"}
1
+ {"version":3,"file":"useAssistantFrameHost.js","names":["Unsubscribe","useEffect","RefObject","AssistantFrameHost","UseAssistantFrameHostOptions","iframeRef","Readonly","HTMLIFrameElement","targetOrigin","register","frameHost","useAssistantFrameHost","active","iframe","window","Window","dispose","syncFrame","current","iframeWindow","isConnected","contentWindow","previous","undefined","disposalFailed","disposalError","error","unsubscribe","onLoad","frameLoaded","addEventListener","removeEventListener","cleanupFailed","cleanupError","console","observer","MutationObserver","observe","document","childList","subtree","disconnect"],"sources":["../../../src/model-context/frame/useAssistantFrameHost.ts"],"sourcesContent":["\"use client\";\n\nimport type { Unsubscribe } from \"@assistant-ui/core\";\nimport { useEffect, type RefObject } from \"react\";\nimport { AssistantFrameHost } from \"@assistant-ui/core\";\n\ntype UseAssistantFrameHostOptions = {\n iframeRef: Readonly<RefObject<HTMLIFrameElement | null | undefined>>;\n targetOrigin?: string;\n register: (frameHost: AssistantFrameHost) => Unsubscribe;\n};\n\n/**\n * React hook that manages the lifecycle of an AssistantFrameHost and its binding to the current AssistantRuntime.\n *\n * Usage example:\n * ```typescript\n * function MyComponent() {\n * const iframeRef = useRef<HTMLIFrameElement>(null);\n *\n * useAssistantFrameHost({\n * iframeRef,\n * targetOrigin: \"https://trusted-domain.com\",\n * });\n *\n * return <iframe ref={iframeRef} src=\"...\" />;\n * }\n * ```\n */\nexport const useAssistantFrameHost = ({\n iframeRef,\n targetOrigin,\n register,\n}: UseAssistantFrameHostOptions): void => {\n useEffect(() => {\n let active:\n | {\n iframe: HTMLIFrameElement;\n window: Window;\n dispose: () => void;\n }\n | undefined;\n\n const syncFrame = () => {\n const iframe = iframeRef.current;\n const iframeWindow =\n iframe?.isConnected === false ? null : iframe?.contentWindow;\n if (active && active.iframe === iframe && active.window === iframeWindow)\n return;\n\n const previous = active;\n active = undefined;\n let disposalFailed = false;\n let disposalError: unknown;\n try {\n previous?.dispose();\n } catch (error) {\n disposalFailed = true;\n disposalError = error;\n }\n\n if (iframe && iframeWindow) {\n const frameHost = new AssistantFrameHost(iframeWindow, targetOrigin);\n let unsubscribe: Unsubscribe;\n try {\n unsubscribe = register(frameHost);\n } catch (error) {\n frameHost.dispose();\n throw error;\n }\n const onLoad = () => frameHost.frameLoaded();\n iframe.addEventListener(\"load\", onLoad);\n active = {\n iframe,\n window: iframeWindow,\n dispose: () => {\n iframe.removeEventListener(\"load\", onLoad);\n let cleanupFailed = false;\n let cleanupError: unknown;\n\n try {\n frameHost.dispose();\n } catch (error) {\n cleanupFailed = true;\n cleanupError = error;\n }\n\n try {\n unsubscribe();\n } catch (error) {\n if (cleanupFailed) {\n console.error(\n \"[assistant-ui] AssistantFrameHost unregistration failed.\",\n error,\n );\n } else {\n cleanupFailed = true;\n cleanupError = error;\n }\n }\n\n if (cleanupFailed) throw cleanupError;\n },\n };\n }\n\n if (disposalFailed) throw disposalError;\n };\n\n syncFrame();\n const observer = new MutationObserver(syncFrame);\n observer.observe(document, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n active?.dispose();\n };\n }, [iframeRef, targetOrigin, register]);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA6BA,MAAaW,yBAAyB,EACpCN,WACAG,cACAC,eACwC;CACxCR,gBAAgB;EACd,IAAIW;EAQJ,MAAMK,kBAAkB;GACtB,MAAMJ,SAASR,UAAUa;GACzB,MAAMC,eACJN,QAAQO,gBAAgB,QAAQ,OAAOP,QAAQQ;GACjD,IAAIT,UAAUA,OAAOC,WAAWA,UAAUD,OAAOE,WAAWK,cAC1D;GAEF,MAAMG,WAAWV;GACjBA,SAASW,KAAAA;GACT,IAAIC,iBAAiB;GACrB,IAAIC;GACJ,IAAI;IACFH,UAAUN,QAAQ;GACpB,SAASU,OAAO;IACdF,iBAAiB;IACjBC,gBAAgBC;GAClB;GAEA,IAAIb,UAAUM,cAAc;IAC1B,MAAMT,YAAY,IAAIP,mBAAmBgB,cAAcX,YAAY;IACnE,IAAImB;IACJ,IAAI;KACFA,cAAclB,SAASC,SAAS;IAClC,SAASgB,SAAO;KACdhB,UAAUM,QAAQ;KAClB,MAAMU;IACR;IACA,MAAME,eAAelB,UAAUmB,YAAY;IAC3ChB,OAAOiB,iBAAiB,QAAQF,MAAM;IACtChB,SAAS;KACPC;KACAC,QAAQK;KACRH,eAAe;MACbH,OAAOkB,oBAAoB,QAAQH,MAAM;MACzC,IAAII,gBAAgB;MACpB,IAAIC;MAEJ,IAAI;OACFvB,UAAUM,QAAQ;MACpB,SAASU,SAAO;OACdM,gBAAgB;OAChBC,eAAeP;MACjB;MAEA,IAAI;OACFC,YAAY;MACd,SAASD,SAAO;OACd,IAAIM,eACFE,QAAQR,MACN,4DACAA,OACF;YACK;QACLM,gBAAgB;QAChBC,eAAeP;OACjB;MACF;MAEA,IAAIM,eAAe,MAAMC;KAC3B;IACF;GACF;GAEA,IAAIT,gBAAgB,MAAMC;EAC5B;EAEAR,UAAU;EACV,MAAMkB,WAAW,IAAIC,iBAAiBnB,SAAS;EAC/CkB,SAASE,QAAQC,UAAU;GAAEC,WAAW;GAAMC,SAAS;EAAK,CAAC;EAE7D,aAAa;GACXL,SAASM,WAAW;GACpB7B,QAAQI,QAAQ;EAClB;CACF,GAAG;EAACX;EAAWG;EAAcC;CAAQ,CAAC;AACxC"}
@@ -1,9 +1,9 @@
1
1
  "use client";
2
+ import { tool } from "@assistant-ui/core";
2
3
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
3
4
  import { createContext, forwardRef, useContext, useEffect, useId, useRef } from "@assistant-ui/tap/react-shim";
4
5
  import { useAui } from "@assistant-ui/store";
5
6
  import { jsx } from "react/jsx-runtime";
6
- import { tool } from "@assistant-ui/core";
7
7
  import { useComposedRefs } from "radix-ui/internal";
8
8
  //#region src/model-context/makeAssistantVisible.tsx
9
9
  const DEFAULT_ACTION_SETTLE_DELAY_MS = 2e3;
@@ -1 +1 @@
1
- {"version":3,"file":"ComposerAddAttachment.d.ts","sourceRoot":"","sources":["../../../src/primitives/composer/ComposerAddAttachment.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEvB,0CAAuC;AAKxC,QAAA,MAAM,wBAAwB,GAAI,gBAE/B;IACD,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3B,wBA4CL,CAAC;AAEF,yBAAiB,8BAA8B,CAAC;IAC9C,KAAY,OAAO,GAAG,mBAAmB,CAAC;IAC1C,KAAY,KAAK,GAAG,iBAAiB,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACxE;AAED,eAAO,MAAM,8BAA8B;;;;;IArDzC,qCAAqC;eAC1B,OAAO,GAAG,SAAS;qDAwD/B,CAAC"}
1
+ {"version":3,"file":"ComposerAddAttachment.d.ts","sourceRoot":"","sources":["../../../src/primitives/composer/ComposerAddAttachment.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAEvB,0CAAuC;AAKxC,QAAA,MAAM,wBAAwB,GAAI,gBAE/B;IACD,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC3B,wBAsDL,CAAC;AAEF,yBAAiB,8BAA8B,CAAC;IAC9C,KAAY,OAAO,GAAG,mBAAmB,CAAC;IAC1C,KAAY,KAAK,GAAG,iBAAiB,CAAC,OAAO,wBAAwB,CAAC,CAAC;CACxE;AAED,eAAO,MAAM,8BAA8B;;;;;IA/DzC,qCAAqC;eAC1B,OAAO,GAAG,SAAS;qDAkE/B,CAAC"}
@@ -2,11 +2,11 @@
2
2
  import { createActionButton } from "../../utils/createActionButton.js";
3
3
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
4
4
  import { useComposerAddAttachment } from "@assistant-ui/core/react";
5
- import "@assistant-ui/tap/react-shim";
5
+ import { useInsertionEffect, useRef } from "@assistant-ui/tap/react-shim";
6
6
  import { useAui } from "@assistant-ui/store";
7
7
  //#region src/primitives/composer/ComposerAddAttachment.ts
8
8
  const useComposerAddAttachment$1 = (t0) => {
9
- const $ = c(6);
9
+ const $ = c(10);
10
10
  let t1;
11
11
  if ($[0] !== t0) {
12
12
  t1 = t0 === void 0 ? {} : t0;
@@ -17,9 +17,26 @@ const useComposerAddAttachment$1 = (t0) => {
17
17
  const multiple = t2 === void 0 ? true : t2;
18
18
  const { disabled, addAttachment } = useComposerAddAttachment();
19
19
  const aui = useAui();
20
+ const scopeGenerationRef = useRef(0);
20
21
  let t3;
21
- if ($[2] !== addAttachment || $[3] !== aui.composer || $[4] !== multiple) {
22
- t3 = () => {
22
+ if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
23
+ t3 = () => () => {
24
+ scopeGenerationRef.current = scopeGenerationRef.current + 1;
25
+ };
26
+ $[2] = t3;
27
+ } else t3 = $[2];
28
+ let t4;
29
+ if ($[3] !== aui.composer || $[4] !== aui.optional.threadListItem) {
30
+ t4 = [aui.composer, aui.optional.threadListItem];
31
+ $[3] = aui.composer;
32
+ $[4] = aui.optional.threadListItem;
33
+ $[5] = t4;
34
+ } else t4 = $[5];
35
+ useInsertionEffect(t3, t4);
36
+ let t5;
37
+ if ($[6] !== addAttachment || $[7] !== aui.composer || $[8] !== multiple) {
38
+ t5 = () => {
39
+ const scopeGeneration = scopeGenerationRef.current;
23
40
  const input = document.createElement("input");
24
41
  input.type = "file";
25
42
  input.multiple = multiple;
@@ -32,6 +49,7 @@ const useComposerAddAttachment$1 = (t0) => {
32
49
  if (!fileList) return;
33
50
  const attachmentPromises = Array.from(fileList, async (file) => {
34
51
  try {
52
+ if (scopeGenerationRef.current !== scopeGeneration) return;
35
53
  await addAttachment(file);
36
54
  } catch {}
37
55
  });
@@ -43,12 +61,12 @@ const useComposerAddAttachment$1 = (t0) => {
43
61
  };
44
62
  input.click();
45
63
  };
46
- $[2] = addAttachment;
47
- $[3] = aui.composer;
48
- $[4] = multiple;
49
- $[5] = t3;
50
- } else t3 = $[5];
51
- const callback = t3;
64
+ $[6] = addAttachment;
65
+ $[7] = aui.composer;
66
+ $[8] = multiple;
67
+ $[9] = t5;
68
+ } else t5 = $[9];
69
+ const callback = t5;
52
70
  if (disabled) return null;
53
71
  return callback;
54
72
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ComposerAddAttachment.js","names":["c","_c","ActionButtonElement","ActionButtonProps","createActionButton","useCallback","useAui","useComposerAddAttachment","useComposerAddAttachmentBehavior","t0","$","t1","undefined","multiple","t2","disabled","addAttachment","aui","t3","composer","input","document","createElement","type","hidden","attachmentAccept","getState","accept","body","appendChild","onchange","e","fileList","target","HTMLInputElement","files","attachmentPromises","Array","from","file","removeChild","Promise","all","oncancel","length","click","callback","ComposerPrimitiveAddAttachment","Element","Props","useComposerAddAttachment","_c","useComposerAddAttachmentBehavior"],"sources":["../../../src/primitives/composer/ComposerAddAttachment.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useComposerAddAttachment as useComposerAddAttachmentBehavior } from \"@assistant-ui/core/react\";\n\nconst useComposerAddAttachment = ({\n multiple = true,\n}: {\n /** allow selecting multiple files */\n multiple?: boolean | undefined;\n} = {}) => {\n const { disabled, addAttachment } = useComposerAddAttachmentBehavior();\n const aui = useAui();\n\n const callback = useCallback(() => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = multiple;\n input.hidden = true;\n\n const attachmentAccept = aui.composer.getState().attachmentAccept;\n if (attachmentAccept !== \"*\") {\n input.accept = attachmentAccept;\n }\n\n document.body.appendChild(input);\n\n input.onchange = async (e) => {\n const fileList = (e.target as HTMLInputElement).files;\n if (!fileList) return;\n\n const attachmentPromises = Array.from(fileList, async (file) => {\n try {\n await addAttachment(file);\n } catch {\n // The composer runtime emits composer.attachmentAddError before rejecting.\n }\n });\n\n document.body.removeChild(input);\n await Promise.all(attachmentPromises);\n };\n\n input.oncancel = () => {\n if (!input.files || input.files.length === 0) {\n document.body.removeChild(input);\n }\n };\n\n input.click();\n }, [aui, multiple, addAttachment]);\n\n if (disabled) return null;\n return callback;\n};\n\nexport namespace ComposerPrimitiveAddAttachment {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useComposerAddAttachment>;\n}\n\nexport const ComposerPrimitiveAddAttachment = createActionButton(\n \"ComposerPrimitive.AddAttachment\",\n useComposerAddAttachment,\n [\"multiple\"],\n);\n"],"mappings":";;;;;;;AAWA,MAAMO,8BAA2BE,OAAA;CAAA,MAAAC,IAAAT,EAAA,CAAA;CAAA,IAAAU;CAAA,IAAAD,EAAA,OAAAD,IAAA;EAACE,KAAAF,OAAAG,KAAAA,IAAA,CAK7B,IAL6BH;EAK5BC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAL4B,MAAA,EAAAG,UAAAC,OAAAH;CAChC,MAAAE,WAAAC,OAAAF,KAAAA,IAAA,OAAAE;CAKA,MAAA,EAAAC,UAAAC,kBAAoCR,yBAAiC;CACrE,MAAAS,MAAYX,OAAO;CAAE,IAAAY;CAAA,IAAAR,EAAA,OAAAM,iBAAAN,EAAA,OAAAO,IAAAE,YAAAT,EAAA,OAAAG,UAAA;EAEQK,WAAA;GAC3B,MAAAE,QAAcC,SAAQC,cAAe,OAAO;GAC5CF,MAAKG,OAAQ;GACbH,MAAKP,WAAYA;GACjBO,MAAKI,SAAU;GAEf,MAAAC,mBAAyBR,IAAGE,SAASO,SAAU,CAAC,CAAAD;GAChD,IAAIA,qBAAqB,KACvBL,MAAKO,SAAUF;GAGjBJ,SAAQO,KAAKC,YAAaT,KAAK;GAE/BA,MAAKU,WAAY,OAAAC,MAAA;IACf,MAAAC,WAAkBD,EAACE,OAA2BE;IAC9C,IAAI,CAACH,UAAQ;IAEb,MAAAI,qBAA2BC,MAAKC,KAAMN,UAAU,OAAAO,SAAA;KAC9C,IAAA;MACE,MAAMvB,cAAcuB,IAAI;KAAC,QAAA,CAAA;IAG1B,CACF;IAEDlB,SAAQO,KAAKY,YAAapB,KAAK;IAC/B,MAAMqB,QAAOC,IAAKN,kBAAkB;GAAC;GAGvChB,MAAKuB,iBAAY;IACf,IAAI,CAACvB,MAAKe,SAAUf,MAAKe,MAAMS,WAAY,GACzCvB,SAAQO,KAAKY,YAAapB,KAAK;GAChC;GAGHA,MAAKyB,MAAO;EAAC;EACdnC,EAAA,KAAAM;EAAAN,EAAA,KAAAO,IAAAE;EAAAT,EAAA,KAAAG;EAAAH,EAAA,KAAAQ;CAAA,OAAAA,KAAAR,EAAA;CApCD,MAAAoC,WAAiB5B;CAsCjB,IAAIH,UAAQ,OAAS;CAAK,OACnB+B;AAAQ;AAQjB,MAAaC,iCAAiC3C,mBAC5C,mCACAG,4BACA,CAAC,UAAU,CACb"}
1
+ {"version":3,"file":"ComposerAddAttachment.js","names":["c","_c","ActionButtonElement","ActionButtonProps","createActionButton","useCallback","useInsertionEffect","useRef","useAui","useComposerAddAttachment","useComposerAddAttachmentBehavior","t0","$","t1","undefined","multiple","t2","disabled","addAttachment","aui","scopeGenerationRef","t3","Symbol","for","current","t4","composer","optional","threadListItem","t5","scopeGeneration","input","document","createElement","type","hidden","attachmentAccept","getState","accept","body","appendChild","onchange","e","fileList","target","HTMLInputElement","files","attachmentPromises","Array","from","file","removeChild","Promise","all","oncancel","length","click","callback","ComposerPrimitiveAddAttachment","Element","Props","useComposerAddAttachment","_c","useComposerAddAttachmentBehavior"],"sources":["../../../src/primitives/composer/ComposerAddAttachment.ts"],"sourcesContent":["\"use client\";\n\nimport {\n type ActionButtonElement,\n type ActionButtonProps,\n createActionButton,\n} from \"../../utils/createActionButton\";\nimport { useCallback, useInsertionEffect, useRef } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { useComposerAddAttachment as useComposerAddAttachmentBehavior } from \"@assistant-ui/core/react\";\n\nconst useComposerAddAttachment = ({\n multiple = true,\n}: {\n /** allow selecting multiple files */\n multiple?: boolean | undefined;\n} = {}) => {\n const { disabled, addAttachment } = useComposerAddAttachmentBehavior();\n const aui = useAui();\n const scopeGenerationRef = useRef(0);\n\n useInsertionEffect(\n () => () => {\n scopeGenerationRef.current += 1;\n },\n [aui.composer, aui.optional.threadListItem],\n );\n\n const callback = useCallback(() => {\n const scopeGeneration = scopeGenerationRef.current;\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.multiple = multiple;\n input.hidden = true;\n\n const attachmentAccept = aui.composer.getState().attachmentAccept;\n if (attachmentAccept !== \"*\") {\n input.accept = attachmentAccept;\n }\n\n document.body.appendChild(input);\n\n input.onchange = async (e) => {\n const fileList = (e.target as HTMLInputElement).files;\n if (!fileList) return;\n\n const attachmentPromises = Array.from(fileList, async (file) => {\n try {\n if (scopeGenerationRef.current !== scopeGeneration) return;\n await addAttachment(file);\n } catch {\n // The composer runtime emits composer.attachmentAddError before rejecting.\n }\n });\n\n document.body.removeChild(input);\n await Promise.all(attachmentPromises);\n };\n\n input.oncancel = () => {\n if (!input.files || input.files.length === 0) {\n document.body.removeChild(input);\n }\n };\n\n input.click();\n }, [aui, multiple, addAttachment]);\n\n if (disabled) return null;\n return callback;\n};\n\nexport namespace ComposerPrimitiveAddAttachment {\n export type Element = ActionButtonElement;\n export type Props = ActionButtonProps<typeof useComposerAddAttachment>;\n}\n\nexport const ComposerPrimitiveAddAttachment = createActionButton(\n \"ComposerPrimitive.AddAttachment\",\n useComposerAddAttachment,\n [\"multiple\"],\n);\n"],"mappings":";;;;;;;AAWA,MAAMS,8BAA2BE,OAAA;CAAA,MAAAC,IAAAX,EAAA,EAAA;CAAA,IAAAY;CAAA,IAAAD,EAAA,OAAAD,IAAA;EAACE,KAAAF,OAAAG,KAAAA,IAAA,CAK7B,IAL6BH;EAK5BC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAL4B,MAAA,EAAAG,UAAAC,OAAAH;CAChC,MAAAE,WAAAC,OAAAF,KAAAA,IAAA,OAAAE;CAKA,MAAA,EAAAC,UAAAC,kBAAoCR,yBAAiC;CACrE,MAAAS,MAAYX,OAAO;CACnB,MAAAY,qBAA2Bb,OAAO,CAAC;CAAE,IAAAc;CAAA,IAAAT,EAAA,OAAAU,OAAAC,IAAA,2BAAA,GAAA;EAGnCF,iBAAM;GACJD,mBAAkBI,UAAlBJ,mBAAkBI,UAAY;EAAJ;EAC3BZ,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAA,IAAAa;CAAA,IAAAb,EAAA,OAAAO,IAAAO,YAAAd,EAAA,OAAAO,IAAAQ,SAAAC,gBAAA;EACDH,KAAA,CAACN,IAAGO,UAAWP,IAAGQ,SAASC,cAAe;EAAChB,EAAA,KAAAO,IAAAO;EAAAd,EAAA,KAAAO,IAAAQ,SAAAC;EAAAhB,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAJ7CN,mBACEe,IAGAI,EACF;CAAC,IAAAI;CAAA,IAAAjB,EAAA,OAAAM,iBAAAN,EAAA,OAAAO,IAAAO,YAAAd,EAAA,OAAAG,UAAA;EAE4Bc,WAAA;GAC3B,MAAAC,kBAAwBV,mBAAkBI;GAC1C,MAAAO,QAAcC,SAAQC,cAAe,OAAO;GAC5CF,MAAKG,OAAQ;GACbH,MAAKhB,WAAYA;GACjBgB,MAAKI,SAAU;GAEf,MAAAC,mBAAyBjB,IAAGO,SAASW,SAAU,CAAC,CAAAD;GAChD,IAAIA,qBAAqB,KACvBL,MAAKO,SAAUF;GAGjBJ,SAAQO,KAAKC,YAAaT,KAAK;GAE/BA,MAAKU,WAAY,OAAAC,MAAA;IACf,MAAAC,WAAkBD,EAACE,OAA2BE;IAC9C,IAAI,CAACH,UAAQ;IAEb,MAAAI,qBAA2BC,MAAKC,KAAMN,UAAU,OAAAO,SAAA;KAC9C,IAAA;MACE,IAAI9B,mBAAkBI,YAAaM,iBAAe;MAClD,MAAMZ,cAAcgC,IAAI;KAAC,QAAA,CAAA;IAG1B,CACF;IAEDlB,SAAQO,KAAKY,YAAapB,KAAK;IAC/B,MAAMqB,QAAOC,IAAKN,kBAAkB;GAAC;GAGvChB,MAAKuB,iBAAY;IACf,IAAI,CAACvB,MAAKe,SAAUf,MAAKe,MAAMS,WAAY,GACzCvB,SAAQO,KAAKY,YAAapB,KAAK;GAChC;GAGHA,MAAKyB,MAAO;EAAC;EACd5C,EAAA,KAAAM;EAAAN,EAAA,KAAAO,IAAAO;EAAAd,EAAA,KAAAG;EAAAH,EAAA,KAAAiB;CAAA,OAAAA,KAAAjB,EAAA;CAtCD,MAAA6C,WAAiB5B;CAwCjB,IAAIZ,UAAQ,OAAS;CAAK,OACnBwC;AAAQ;AAQjB,MAAaC,iCAAiCtD,mBAC5C,mCACAK,4BACA,CAAC,UAAU,CACb"}
@@ -20,7 +20,10 @@ export declare namespace ComposerPrimitiveInput {
20
20
  */
21
21
  cancelOnEscape?: boolean | undefined;
22
22
  /**
23
- * Whether to automatically focus the input when a new run starts.
23
+ * Whether to focus the input when a new run starts if nothing is focused
24
+ * or focus is already inside the same thread or composer. Keeps the current
25
+ * selection when the input is already focused. Applies when `autoFocus` is
26
+ * enabled.
24
27
  *
25
28
  * @deprecated Experimental since 2024-10-12. Not scheduled for removal; the API may change in any release.
26
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"ComposerInput.d.ts","sourceRoot":"","sources":["../../../src/primitives/composer/ComposerInput.tsx"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,YAAY,EAOlB,MAAM,OAAO,CAAC;AACf,OAAyB,EACvB,KAAK,qBAAqB,EAE3B,MAAM,yBAAyB,CAAC;AAmBjC,yBAAiB,sBAAsB,CAAC;IACtC,MAAM,MAAM,OAAO,GAAG,mBAAmB,CAAC;IAE1C,KAAK,SAAS,GAAG;QACf;;;WAGG;QACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC9B;;WAEG;QACH,MAAM,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;QAClC;;;;;WAKG;QACH,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrC;;;;;;WAMG;QACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/C;;;;;;WAMG;QACH,8BAA8B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrD;;;;;;WAMG;QACH,8BAA8B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrD;;;;;;;;;WASG;QACH,kCAAkC,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACzD;;;WAGG;QACH,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAC5C,CAAC;IAEF,KAAK,eAAe,GAChB;QACE;;;;;;WAMG;QACH,UAAU,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;QACxD;;;WAGG;QACH,aAAa,CAAC,EAAE,KAAK,CAAC;KACvB,GACD;QACE,UAAU,CAAC,EAAE,KAAK,CAAC;QACnB;;;;WAIG;QACH,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KACrC,CAAC;IAEN,MAAM,MAAM,KAAK,GAAG,qBAAqB,GAAG,SAAS,GAAG,eAAe,CAAC;;CACzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,sBAAsB,8HAoTlC,CAAC"}
1
+ {"version":3,"file":"ComposerInput.d.ts","sourceRoot":"","sources":["../../../src/primitives/composer/ComposerInput.tsx"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,YAAY,EAOlB,MAAM,OAAO,CAAC;AACf,OAAyB,EACvB,KAAK,qBAAqB,EAE3B,MAAM,yBAAyB,CAAC;AAoBjC,yBAAiB,sBAAsB,CAAC;IACtC,MAAM,MAAM,OAAO,GAAG,mBAAmB,CAAC;IAE1C,KAAK,SAAS,GAAG;QACf;;;WAGG;QACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC9B;;WAEG;QACH,MAAM,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;QAClC;;;;;WAKG;QACH,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrC;;;;;;;;;WASG;QACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/C;;;;;;WAMG;QACH,8BAA8B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrD;;;;;;WAMG;QACH,8BAA8B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrD;;;;;;;;;WASG;QACH,kCAAkC,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACzD;;;WAGG;QACH,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAC5C,CAAC;IAEF,KAAK,eAAe,GAChB;QACE;;;;;;WAMG;QACH,UAAU,CAAC,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,CAAC;QACxD;;;WAGG;QACH,aAAa,CAAC,EAAE,KAAK,CAAC;KACvB,GACD;QACE,UAAU,CAAC,EAAE,KAAK,CAAC;QACnB;;;;WAIG;QACH,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KACrC,CAAC;IAEN,MAAM,MAAM,KAAK,GAAG,qBAAqB,GAAG,SAAS,GAAG,eAAe,CAAC;;CACzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,sBAAsB,8HA0UlC,CAAC"}
@@ -5,6 +5,7 @@ import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom.js";
5
5
  import { useMediaQuery } from "../../utils/hooks/useMediaQuery.js";
6
6
  import { isCompositionKey } from "../../utils/isCompositionKey.js";
7
7
  import { useComposerInputPluginRegistryOptional } from "./ComposerInputPluginContext.js";
8
+ import { useThreadRootElementRef } from "../thread/ThreadRootElementContext.js";
8
9
  import { useComposerCancelWithFocus } from "./useComposerCancelWithFocus.js";
9
10
  import { useComposerInputDisabled, useComposerInputValue, useTriggerPopoverAriaProps } from "./useComposerInputState.js";
10
11
  import { forwardRef, isValidElement, useCallback, useEffect, useRef } from "@assistant-ui/tap/react-shim";
@@ -52,6 +53,7 @@ const TOUCH_PRIMARY_QUERY = "(pointer: coarse) and (not (any-pointer: fine))";
52
53
  */
53
54
  const ComposerPrimitiveInput = forwardRef(({ autoFocus = false, asChild, render, disabled: disabledProp, onChange, onKeyDown, onPaste, onSelect, submitOnEnter, submitMode, cancelOnEscape = true, unstable_focusOnRunStart = true, unstable_focusOnScrollToBottom = true, unstable_focusOnThreadSwitched = true, unstable_insertNewlineOnTouchEnter = false, addAttachmentOnPaste = true, onHeightChange, ...rest }, forwardedRef) => {
54
55
  const aui = useAui();
56
+ const threadRootRef = useThreadRootElementRef();
55
57
  const pluginRegistry = useComposerInputPluginRegistryOptional();
56
58
  const compactContext = useComposerCompactContextOptional();
57
59
  const declaredSubmitMode = submitMode ?? (submitOnEnter === false ? "none" : "enter");
@@ -129,16 +131,27 @@ const ComposerPrimitiveInput = forwardRef(({ autoFocus = false, asChild, render,
129
131
  textarea.focus({ preventScroll: true });
130
132
  textarea.setSelectionRange(textarea.value.length, textarea.value.length);
131
133
  }, [autoFocusEnabled]);
134
+ const focusOnRunStart = useCallback(() => {
135
+ const textarea_0 = textareaRef.current;
136
+ if (!textarea_0) return;
137
+ const doc = textarea_0.ownerDocument;
138
+ const active = doc.activeElement;
139
+ if (active === textarea_0) return;
140
+ const root = threadRootRef?.current;
141
+ const composerRoot = textarea_0.closest("[data-aui-composer-type]");
142
+ if (active && active !== doc.body && !composerRoot?.contains(active) && (!root?.contains(active) || active.closest("[data-aui-thread-root]") !== root)) return;
143
+ focus();
144
+ }, [focus, threadRootRef]);
132
145
  useEffect(() => focus(), [focus]);
133
146
  useOnScrollToBottom(() => {
134
147
  if (aui.composer.getState().type === "thread" && unstable_focusOnScrollToBottom) focus();
135
148
  });
136
149
  useEffect(() => {
137
150
  if (aui.composer.getState().type !== "thread" || !unstable_focusOnRunStart) return void 0;
138
- return aui.on("thread.runStart", focus);
151
+ return aui.on("thread.runStart", focusOnRunStart);
139
152
  }, [
140
153
  unstable_focusOnRunStart,
141
- focus,
154
+ focusOnRunStart,
142
155
  aui
143
156
  ]);
144
157
  useEffect(() => {