@assistant-ui/react 0.15.23 → 0.15.24
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.
- package/README.md +44 -23
- package/dist/assistant-transport.js.map +1 -1
- package/dist/cloud-renderer/CloudRendererHost.js.map +1 -1
- package/dist/context/providers/MessageProvider.js.map +1 -1
- package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
- package/dist/hooks/useToolCallElapsed.js.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/model-context/frame/useAssistantFrameHost.js.map +1 -1
- package/dist/model-context/makeAssistantVisible.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarCopy.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarEdit.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarExportMarkdown.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackNegative.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarFeedbackPositive.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarReload.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarRoot.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarSpeak.js.map +1 -1
- package/dist/primitives/actionBar/ActionBarStopSpeaking.js.map +1 -1
- package/dist/primitives/actionBar/useActionBarFloatStatus.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreContent.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreItem.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreRoot.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreSeparator.js.map +1 -1
- package/dist/primitives/actionBarMore/ActionBarMoreTrigger.js.map +1 -1
- package/dist/primitives/actionBarMore/scope.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalAnchor.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalContent.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalTrigger.js.map +1 -1
- package/dist/primitives/assistantModal/popoverRenderPrimitives.js.map +1 -1
- package/dist/primitives/assistantModal/scope.js.map +1 -1
- package/dist/primitives/attachment/AttachmentName.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRemove.js.map +1 -1
- package/dist/primitives/attachment/AttachmentRoot.js.map +1 -1
- package/dist/primitives/attachment/AttachmentThumb.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerCount.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNext.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerNumber.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerPrevious.js.map +1 -1
- package/dist/primitives/branchPicker/BranchPickerRoot.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtAccordionTrigger.js.map +1 -1
- package/dist/primitives/chainOfThought/ChainOfThoughtRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/composer/ComposerCancel.js.map +1 -1
- package/dist/primitives/composer/ComposerDictate.js.map +1 -1
- package/dist/primitives/composer/ComposerDictationTranscript.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +3 -2
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -1
- package/dist/primitives/composer/ComposerQuote.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerSend.js.map +1 -1
- package/dist/primitives/composer/ComposerStopDictation.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopover.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverBack.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverCategories.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverItems.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverResource.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverRootContext.js.map +1 -1
- package/dist/primitives/composer/trigger/index.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerDetectionResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerSelectionResource.js.map +1 -1
- package/dist/primitives/composer/useComposerInputState.js.map +1 -1
- package/dist/primitives/dropdownMenuRenderPrimitives.js.map +1 -1
- package/dist/primitives/error/ErrorMessage.js.map +1 -1
- package/dist/primitives/error/ErrorRoot.js.map +1 -1
- package/dist/primitives/message/MessageIf.js.map +1 -1
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.js +42 -17
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartImage.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemRemove.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemSteer.js.map +1 -1
- package/dist/primitives/queueItem/QueueItemText.js.map +1 -1
- package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarQuote.js.map +1 -1
- package/dist/primitives/selectionToolbar/SelectionToolbarRoot.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionDescription.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTitle.js.map +1 -1
- package/dist/primitives/suggestion/SuggestionTrigger.js.map +1 -1
- package/dist/primitives/thread/ThreadIf.js.map +1 -1
- package/dist/primitives/thread/ThreadRoot.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadRoot.js +2 -0
- package/dist/primitives/thread/ThreadRoot.js.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
- package/dist/primitives/thread/ThreadSuggestion.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -1
- package/dist/primitives/thread/topAnchor/useTopAnchorReserve.js.map +1 -1
- package/dist/primitives/threadList/ThreadListLoadMore.js.map +1 -1
- package/dist/primitives/threadList/ThreadListNew.js.map +1 -1
- package/dist/primitives/threadList/ThreadListRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemArchive.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemDelete.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemRoot.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemTrigger.js.map +1 -1
- package/dist/primitives/threadListItem/ThreadListItemUnarchive.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreContent.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreItem.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreRoot.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreSeparator.js.map +1 -1
- package/dist/primitives/threadListItemMore/ThreadListItemMoreTrigger.js.map +1 -1
- package/dist/primitives/threadListItemMore/scope.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useModelContextSnapshot.js.map +1 -1
- package/dist/unstable/webmcp/WebMcpRegistrationResource.js.map +1 -1
- package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -1
- package/dist/utils/Primitive.js.map +1 -1
- package/dist/utils/createActionButton.js.map +1 -1
- package/dist/utils/hooks/useManagedRef.js.map +1 -1
- package/dist/utils/hooks/useMediaQuery.js.map +1 -1
- package/dist/utils/hooks/useOnResizeContent.js.map +1 -1
- package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
- package/dist/utils/hooks/useSizeHandle.js.map +1 -1
- package/dist/utils/isCompositionKey.d.ts +2 -0
- package/dist/utils/isCompositionKey.d.ts.map +1 -0
- package/dist/utils/isCompositionKey.js +6 -0
- package/dist/utils/isCompositionKey.js.map +1 -0
- package/dist/utils/smooth/SmoothContext.js.map +1 -1
- package/package.json +11 -11
- package/src/primitives/composer/ComposerInput.test.tsx +33 -1
- package/src/primitives/composer/ComposerInput.tsx +3 -2
- package/src/primitives/message/MessagePartsGrouped.test.tsx +271 -1
- package/src/primitives/message/MessagePartsGrouped.tsx +14 -6
- package/src/primitives/thread/ThreadRoot.test.tsx +33 -0
- package/src/primitives/thread/ThreadRoot.tsx +3 -0
- package/src/utils/isCompositionKey.ts +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createActionButton.js","names":["ComponentRef","forwardRef","ComponentPropsWithoutRef","MouseEventHandler","Primitive","composeEventHandlers","ActionButtonCallback","props","TProps","HTMLButtonElement","PrimitiveButtonProps","button","ActionButtonProps","THook","ActionButtonElement","createActionButton","displayName","useActionButton","forwardProps","NonNullable","ActionButton","forwardedRef","$","_c","forwardedProps","primitiveProps","Object","keys","Array","forEach","key","includes","callback","undefined","t0","t1","t2","disabled","t3","onClick","t4"],"sources":["../../src/utils/createActionButton.tsx"],"sourcesContent":["import {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEventHandler,\n} from \"react\";\nimport { Primitive } from \"./Primitive\";\nimport { composeEventHandlers } from \"radix-ui/internal\";\n\ntype ActionButtonCallback<TProps> = (\n props: TProps,\n) => MouseEventHandler<HTMLButtonElement> | null;\n\ntype PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;\n\nexport type ActionButtonProps<THook> = PrimitiveButtonProps &\n (THook extends (props: infer TProps) => unknown ? TProps : never);\n\nexport type ActionButtonElement = ComponentRef<typeof Primitive.button>;\n\nexport const createActionButton = <TProps,>(\n displayName: string,\n useActionButton: ActionButtonCallback<TProps>,\n forwardProps: (keyof NonNullable<TProps>)[] = [],\n) => {\n const ActionButton = forwardRef<\n ActionButtonElement,\n PrimitiveButtonProps & TProps\n >((props, forwardedRef) => {\n const forwardedProps = {} as TProps;\n const primitiveProps = {} as PrimitiveButtonProps;\n\n (Object.keys(props) as Array<keyof typeof props>).forEach((key) => {\n if (forwardProps.includes(key as keyof TProps)) {\n (forwardedProps as any)[key] = props[key];\n } else {\n (primitiveProps as any)[key] = props[key];\n }\n });\n\n const callback = useActionButton(forwardedProps as TProps) ?? undefined;\n return (\n <Primitive.button\n type=\"button\"\n {...primitiveProps}\n ref={forwardedRef}\n disabled={primitiveProps.disabled || !callback}\n onClick={composeEventHandlers(primitiveProps.onClick, callback)}\n />\n );\n });\n\n ActionButton.displayName = displayName;\n\n return ActionButton;\n};\n"],"mappings":";;;;;;AAoBA,MAAae,sBACXC,aACAC,iBACAC,eAA8C,CAAA,MAC3C;CACH,MAAME,eAAenB,YAGnBM,OAAAc,iBAAA;EAAA,MAAAC,IAAAC,EAAA,CAAA;EACA,MAAAC,iBAAuB,CAAC;EACxB,MAAAC,iBAAuB,CAAC;EAExB,OAAOE,KAAMpB,KAAK,CAAC,CAA6BsB,SAAUC,QAAA;GACxD,IAAIZ,aAAYa,SAAUD,GAAmB,GAC3C,eAAwBA,OAAOvB,MAAMuB;QAErC,eAAwBA,OAAOvB,MAAMuB;EACtC,CACF;EAED,MAAAE,WAAiBf,gBAAgBO,cAAqC,KAArDS,KAAAA;EAEd,MAAAC,KAAA9B;EACM,MAAA+B,KAAA;EAGK,MAAAC,KAAAX,eAAcY,YAAd,CAA4BL;EAC7B,MAAAM,KAAAjC,qBAAqBoB,eAAcc,SAAUP,QAAQ;EAAC,IAAAQ;EAAA,IAAAlB,EAAA,OAAAD,gBAAAC,EAAA,OAAAG,kBAAAH,EAAA,OAAAY,GAAAvB,UAAAW,EAAA,OAAAc,MAAAd,EAAA,OAAAgB,IAAA;GALjEE,KAAA,oBAAA,GAAA,QAAA;IACO,MAAAL;IAAQ,GACTV;IACCJ,KAAAA;IACK,UAAAe;IACD,SAAAE;GAAsD,CAAA;GAC/DhB,EAAA,KAAAD;GAAAC,EAAA,KAAAG;GAAAH,EAAA,KAAAY,GAAAvB;GAAAW,EAAA,KAAAc;GAAAd,EAAA,KAAAgB;GAAAhB,EAAA,KAAAkB;EAAA,OAAAA,KAAAlB,EAAA;EAAA,OANFkB;CAME,CAEL;CAEDpB,aAAaJ,cAAcA;CAE3B,OAAOI;AACT"}
|
|
1
|
+
{"version":3,"file":"createActionButton.js","names":["ComponentRef","forwardRef","ComponentPropsWithoutRef","MouseEventHandler","Primitive","composeEventHandlers","ActionButtonCallback","props","TProps","HTMLButtonElement","PrimitiveButtonProps","button","ActionButtonProps","THook","ActionButtonElement","createActionButton","displayName","useActionButton","forwardProps","NonNullable","ActionButton","forwardedRef","$","_c","forwardedProps","primitiveProps","Object","keys","Array","forEach","key","includes","callback","undefined","t0","t1","t2","disabled","t3","onClick","t4","_c","Primitive"],"sources":["../../src/utils/createActionButton.tsx"],"sourcesContent":["import {\n type ComponentRef,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEventHandler,\n} from \"react\";\nimport { Primitive } from \"./Primitive\";\nimport { composeEventHandlers } from \"radix-ui/internal\";\n\ntype ActionButtonCallback<TProps> = (\n props: TProps,\n) => MouseEventHandler<HTMLButtonElement> | null;\n\ntype PrimitiveButtonProps = ComponentPropsWithoutRef<typeof Primitive.button>;\n\nexport type ActionButtonProps<THook> = PrimitiveButtonProps &\n (THook extends (props: infer TProps) => unknown ? TProps : never);\n\nexport type ActionButtonElement = ComponentRef<typeof Primitive.button>;\n\nexport const createActionButton = <TProps,>(\n displayName: string,\n useActionButton: ActionButtonCallback<TProps>,\n forwardProps: (keyof NonNullable<TProps>)[] = [],\n) => {\n const ActionButton = forwardRef<\n ActionButtonElement,\n PrimitiveButtonProps & TProps\n >((props, forwardedRef) => {\n const forwardedProps = {} as TProps;\n const primitiveProps = {} as PrimitiveButtonProps;\n\n (Object.keys(props) as Array<keyof typeof props>).forEach((key) => {\n if (forwardProps.includes(key as keyof TProps)) {\n (forwardedProps as any)[key] = props[key];\n } else {\n (primitiveProps as any)[key] = props[key];\n }\n });\n\n const callback = useActionButton(forwardedProps as TProps) ?? undefined;\n return (\n <Primitive.button\n type=\"button\"\n {...primitiveProps}\n ref={forwardedRef}\n disabled={primitiveProps.disabled || !callback}\n onClick={composeEventHandlers(primitiveProps.onClick, callback)}\n />\n );\n });\n\n ActionButton.displayName = displayName;\n\n return ActionButton;\n};\n"],"mappings":";;;;;;AAoBA,MAAae,sBACXC,aACAC,iBACAC,eAA8C,CAAA,MAC3C;CACH,MAAME,eAAenB,YAGnBM,OAAAc,iBAAA;EAAA,MAAAC,IAAAC,EAAA,CAAA;EACA,MAAAC,iBAAuB,CAAC;EACxB,MAAAC,iBAAuB,CAAC;EAExB,OAAOE,KAAMpB,KAAK,CAAC,CAA6BsB,SAAUC,QAAA;GACxD,IAAIZ,aAAYa,SAAUD,GAAmB,GAC3C,eAAwBA,OAAOvB,MAAMuB;QAErC,eAAwBA,OAAOvB,MAAMuB;EACtC,CACF;EAED,MAAAE,WAAiBf,gBAAgBO,cAAqC,KAArDS,KAAAA;EAEd,MAAAC,KAAA9B;EACM,MAAA+B,KAAA;EAGK,MAAAC,KAAAX,eAAcY,YAAd,CAA4BL;EAC7B,MAAAM,KAAAjC,qBAAqBoB,eAAcc,SAAUP,QAAQ;EAAC,IAAAQ;EAAA,IAAAlB,EAAA,OAAAD,gBAAAC,EAAA,OAAAG,kBAAAH,EAAA,OAAAY,GAAAvB,UAAAW,EAAA,OAAAc,MAAAd,EAAA,OAAAgB,IAAA;GALjEE,KAAA,oBAAA,GAAA,QAAA;IACO,MAAAL;IAAQ,GACTV;IACCJ,KAAAA;IACK,UAAAe;IACD,SAAAE;GAAsD,CAAA;GAC/DhB,EAAA,KAAAD;GAAAC,EAAA,KAAAG;GAAAH,EAAA,KAAAY,GAAAvB;GAAAW,EAAA,KAAAc;GAAAd,EAAA,KAAAgB;GAAAhB,EAAA,KAAAkB;EAAA,OAAAA,KAAAlB,EAAA;EAAA,OANFkB;CAME,CAEL;CAEDpB,aAAaJ,cAAcA;CAE3B,OAAOI;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useManagedRef.js","names":["useCallback","useRef","useManagedRef","callback","$","_c","cleanupRef","undefined","t0","el","current","ref"],"sources":["../../../src/utils/hooks/useManagedRef.ts"],"sourcesContent":["import { useCallback, useRef } from \"react\";\n\nexport const useManagedRef = <TNode>(\n callback: (node: TNode) => (() => void) | undefined,\n) => {\n const cleanupRef = useRef<(() => void) | undefined>(undefined);\n\n const ref = useCallback(\n (el: TNode | null) => {\n // Call the previous cleanup function\n if (cleanupRef.current) {\n cleanupRef.current();\n cleanupRef.current = undefined;\n }\n\n // Call the new callback and store its cleanup function\n if (el) {\n cleanupRef.current = callback(el);\n }\n },\n [callback],\n );\n\n return ref;\n};\n"],"mappings":";;;AAEA,MAAaE,iBAAgBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAG3B,MAAAC,aAAmBL,OAAiCM,KAAAA,CAAS;CAAE,IAAAC;CAAA,IAAAJ,EAAA,OAAAD,UAAA;EAG7DK,MAAAC,OAAA;GAEE,IAAIH,WAAUI,SAAQ;IACpBJ,WAAUI,QAAS;IACnBJ,WAAUI,UAAWH,KAAAA;GAAH;GAIpB,IAAIE,IACFH,WAAUI,UAAWP,SAASM,EAAE;EACjC;EACFL,EAAA,KAAAD;EAAAC,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAED,OAEKO;AAAG"}
|
|
1
|
+
{"version":3,"file":"useManagedRef.js","names":["useCallback","useRef","useManagedRef","callback","$","_c","cleanupRef","undefined","t0","el","current","ref","_c","ref"],"sources":["../../../src/utils/hooks/useManagedRef.ts"],"sourcesContent":["import { useCallback, useRef } from \"react\";\n\nexport const useManagedRef = <TNode>(\n callback: (node: TNode) => (() => void) | undefined,\n) => {\n const cleanupRef = useRef<(() => void) | undefined>(undefined);\n\n const ref = useCallback(\n (el: TNode | null) => {\n // Call the previous cleanup function\n if (cleanupRef.current) {\n cleanupRef.current();\n cleanupRef.current = undefined;\n }\n\n // Call the new callback and store its cleanup function\n if (el) {\n cleanupRef.current = callback(el);\n }\n },\n [callback],\n );\n\n return ref;\n};\n"],"mappings":";;;AAEA,MAAaE,iBAAgBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAG3B,MAAAC,aAAmBL,OAAiCM,KAAAA,CAAS;CAAE,IAAAC;CAAA,IAAAJ,EAAA,OAAAD,UAAA;EAG7DK,MAAAC,OAAA;GAEE,IAAIH,WAAUI,SAAQ;IACpBJ,WAAUI,QAAS;IACnBJ,WAAUI,UAAWH,KAAAA;GAAH;GAIpB,IAAIE,IACFH,WAAUI,UAAWP,SAASM,EAAE;EACjC;EACFL,EAAA,KAAAD;EAAAC,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAED,OAEKO;AAAG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMediaQuery.js","names":["c","_c","useCallback","useSyncExternalStore","getServerSnapshot","noopUnsubscribe","useMediaQuery","query","$","t0","callback","window","matchMedia","mql","addEventListener","removeEventListener","subscribe","t1","matches","getSnapshot"],"sources":["../../../src/utils/hooks/useMediaQuery.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nconst getServerSnapshot = () => false;\nconst noopUnsubscribe = () => {};\n\nexport const useMediaQuery = (query: string | null): boolean => {\n const subscribe = useCallback(\n (callback: () => void) => {\n if (typeof window === \"undefined\" || query === null || !window.matchMedia)\n return noopUnsubscribe;\n const mql = window.matchMedia(query);\n mql.addEventListener(\"change\", callback);\n return () => mql.removeEventListener(\"change\", callback);\n },\n [query],\n );\n\n const getSnapshot = useCallback(() => {\n if (typeof window === \"undefined\" || query === null || !window.matchMedia)\n return false;\n return window.matchMedia(query).matches;\n }, [query]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n};\n"],"mappings":";;;;AAIA,MAAMI,0BAA0B;AAChC,MAAMC,wBAAwB,CAAC;AAE/B,MAAaC,iBAAgBC,UAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,OAAA;EAEzBE,MAAAC,aAAA;GACE,IAAI,OAAOC,WAAW,eAAeJ,UAAU,QAA3C,CAAoDI,OAAMC,YAAW,OAChEP;GACT,MAAAQ,MAAYF,OAAMC,WAAYL,KAAK;GACnCM,IAAGC,iBAAkB,UAAUJ,QAAQ;GAAC,aAC3BG,IAAGE,oBAAqB,UAAUL,QAAQ;EAAC;EACzDF,EAAA,KAAAD;EAAAC,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAPH,MAAAQ,YAAkBP;CAShB,IAAAQ;CAAA,IAAAT,EAAA,OAAAD,OAAA;EAE8BU,WAAA;GAC9B,IAAI,OAAON,WAAW,eAAeJ,UAAU,QAA3C,CAAoDI,OAAMC,YAAW,OAChE;GAAM,OACRD,OAAMC,WAAYL,KAAK,CAAC,CAAAW;EAAQ;EACxCV,EAAA,KAAAD;EAAAC,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAW,OAELL,qBAAqBa,WAAWG,IAAaf,iBAAiB;AAAC"}
|
|
1
|
+
{"version":3,"file":"useMediaQuery.js","names":["c","_c","useCallback","useSyncExternalStore","getServerSnapshot","noopUnsubscribe","useMediaQuery","query","$","t0","callback","window","matchMedia","mql","addEventListener","removeEventListener","subscribe","t1","matches","getSnapshot","_c","getSnapshot"],"sources":["../../../src/utils/hooks/useMediaQuery.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback, useSyncExternalStore } from \"react\";\n\nconst getServerSnapshot = () => false;\nconst noopUnsubscribe = () => {};\n\nexport const useMediaQuery = (query: string | null): boolean => {\n const subscribe = useCallback(\n (callback: () => void) => {\n if (typeof window === \"undefined\" || query === null || !window.matchMedia)\n return noopUnsubscribe;\n const mql = window.matchMedia(query);\n mql.addEventListener(\"change\", callback);\n return () => mql.removeEventListener(\"change\", callback);\n },\n [query],\n );\n\n const getSnapshot = useCallback(() => {\n if (typeof window === \"undefined\" || query === null || !window.matchMedia)\n return false;\n return window.matchMedia(query).matches;\n }, [query]);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n};\n"],"mappings":";;;;AAIA,MAAMI,0BAA0B;AAChC,MAAMC,wBAAwB,CAAC;AAE/B,MAAaC,iBAAgBC,UAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,OAAA;EAEzBE,MAAAC,aAAA;GACE,IAAI,OAAOC,WAAW,eAAeJ,UAAU,QAA3C,CAAoDI,OAAMC,YAAW,OAChEP;GACT,MAAAQ,MAAYF,OAAMC,WAAYL,KAAK;GACnCM,IAAGC,iBAAkB,UAAUJ,QAAQ;GAAC,aAC3BG,IAAGE,oBAAqB,UAAUL,QAAQ;EAAC;EACzDF,EAAA,KAAAD;EAAAC,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAPH,MAAAQ,YAAkBP;CAShB,IAAAQ;CAAA,IAAAT,EAAA,OAAAD,OAAA;EAE8BU,WAAA;GAC9B,IAAI,OAAON,WAAW,eAAeJ,UAAU,QAA3C,CAAoDI,OAAMC,YAAW,OAChE;GAAM,OACRD,OAAMC,WAAYL,KAAK,CAAC,CAAAW;EAAQ;EACxCV,EAAA,KAAAD;EAAAC,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAW,OAELL,qBAAqBa,WAAWG,IAAaf,iBAAiB;AAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","resizeObserver","ResizeObserver","mutationObserver","MutationObserver","mutations","hasRelevantMutation","some","_temp","observe","childList","subtree","attributes","characterData","disconnect","refCallback","m","type","attributeName"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { useCallbackRef } from \"radix-ui/internal\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => {\n const resizeObserver = new ResizeObserver(() => {\n callbackRef();\n });\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // with code paths that write styles in response to viewport changes.\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n callbackRef();\n }\n });\n\n resizeObserver.observe(el);\n mutationObserver.observe(el, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n },\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;;AAIA,MAAaG,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAA;GACE,MAAAC,iBAAuB,IAAIC,qBAAe;IACxCJ,YAAY;GAAC,CACd;GAED,MAAAK,mBAAyB,IAAIC,kBAAiBC,cAAA;IAM5C,IAH4BA,UAASE,KACnCC,KAEEF,GACFR,YAAY;GACb,CACF;GAEDG,eAAcQ,QAAST,EAAE;GACzBG,iBAAgBM,QAAST,IAAI;IAAAU,WAChB;IAAIC,SACN;IAAIC,YACD;IAAIC,eACD;GACjB,CAAC;GAAC,aAEK;IACLZ,eAAca,WAAY;IAC1BX,iBAAgBW,WAAY;GAAC;EAC9B;EACFlB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAED,OAEKH,cAAcsB,EAAW;AAAC;AApCD,SAAAP,MAAAQ,GAAA;CAAA,OAajBA,EAACC,SAAU,gBAAgBD,EAACE,kBAAmB;AAAO"}
|
|
1
|
+
{"version":3,"file":"useOnResizeContent.js","names":["useCallbackRef","useCallback","useManagedRef","useOnResizeContent","callback","$","_c","callbackRef","t0","el","resizeObserver","ResizeObserver","mutationObserver","MutationObserver","mutations","hasRelevantMutation","some","_temp","observe","childList","subtree","attributes","characterData","disconnect","refCallback","m","type","attributeName","_c","refCallback"],"sources":["../../../src/utils/hooks/useOnResizeContent.ts"],"sourcesContent":["import { useCallbackRef } from \"radix-ui/internal\";\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\n\nexport const useOnResizeContent = (callback: () => void) => {\n const callbackRef = useCallbackRef(callback);\n\n const refCallback = useCallback(\n (el: HTMLElement) => {\n const resizeObserver = new ResizeObserver(() => {\n callbackRef();\n });\n\n const mutationObserver = new MutationObserver((mutations) => {\n // Filter out style-only attribute mutations to prevent feedback loops\n // with code paths that write styles in response to viewport changes.\n const hasRelevantMutation = mutations.some(\n (m) => m.type !== \"attributes\" || m.attributeName !== \"style\",\n );\n if (hasRelevantMutation) {\n callbackRef();\n }\n });\n\n resizeObserver.observe(el);\n mutationObserver.observe(el, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n });\n\n return () => {\n resizeObserver.disconnect();\n mutationObserver.disconnect();\n };\n },\n [callbackRef],\n );\n\n return useManagedRef(refCallback);\n};\n"],"mappings":";;;;;AAIA,MAAaG,sBAAqBC,aAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAChC,MAAAC,cAAoBP,eAAeI,QAAQ;CAAE,IAAAI;CAAA,IAAAH,EAAA,OAAAE,aAAA;EAG3CC,MAAAC,OAAA;GACE,MAAAC,iBAAuB,IAAIC,qBAAe;IACxCJ,YAAY;GAAC,CACd;GAED,MAAAK,mBAAyB,IAAIC,kBAAiBC,cAAA;IAM5C,IAH4BA,UAASE,KACnCC,KAEEF,GACFR,YAAY;GACb,CACF;GAEDG,eAAcQ,QAAST,EAAE;GACzBG,iBAAgBM,QAAST,IAAI;IAAAU,WAChB;IAAIC,SACN;IAAIC,YACD;IAAIC,eACD;GACjB,CAAC;GAAC,aAEK;IACLZ,eAAca,WAAY;IAC1BX,iBAAgBW,WAAY;GAAC;EAC9B;EACFlB,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAAA,KAAAH,EAAA;CAED,OAEKH,cAAcsB,EAAW;AAAC;AApCD,SAAAP,MAAAQ,GAAA;CAAA,OAajBA,EAACC,SAAU,gBAAgBD,EAACE,kBAAmB;AAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOnScrollToBottom.js","names":["c","_c","useCallbackRef","useEffect","useThreadViewport","useOnScrollToBottom","callback","$","callbackRef","onScrollToBottom","_temp","t0","t1","vp"],"sources":["../../../src/utils/hooks/useOnScrollToBottom.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallbackRef } from \"radix-ui/internal\";\nimport { useEffect } from \"react\";\nimport { useThreadViewport } from \"../../context/react/ThreadViewportContext\";\n\nexport const useOnScrollToBottom = (\n callback: (config: { behavior: ScrollBehavior }) => void,\n) => {\n const callbackRef = useCallbackRef(callback);\n const onScrollToBottom = useThreadViewport((vp) => vp.onScrollToBottom);\n\n useEffect(() => {\n return onScrollToBottom(callbackRef);\n }, [onScrollToBottom, callbackRef]);\n};\n"],"mappings":";;;;;;AAMA,MAAaK,uBAAsBC,aAAA;CAAA,MAAAC,IAAAN,EAAA,CAAA;CAGjC,MAAAO,cAAoBN,eAAeI,QAAQ;CAC3C,MAAAG,mBAAyBL,kBAAkBM,KAA2B;CAAE,IAAAC;CAAA,IAAAC;CAAA,IAAAL,EAAA,OAAAC,eAAAD,EAAA,OAAAE,kBAAA;EAE9DE,WACDF,iBAAiBD,WAAW;EAClCI,KAAA,CAACH,kBAAkBD,WAAW;EAACD,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;CAAA,OAAA;EAAAD,KAAAJ,EAAA;EAAAK,KAAAL,EAAA;CAAA;CAFlCJ,UAAUQ,IAEPC,EAA+B;AAAC;AARF,SAAAF,MAAAG,IAAA;CAAA,OAIkBA,GAAEJ;AAAiB"}
|
|
1
|
+
{"version":3,"file":"useOnScrollToBottom.js","names":["c","_c","useCallbackRef","useEffect","useThreadViewport","useOnScrollToBottom","callback","$","callbackRef","onScrollToBottom","_temp","t0","t1","vp","_c"],"sources":["../../../src/utils/hooks/useOnScrollToBottom.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallbackRef } from \"radix-ui/internal\";\nimport { useEffect } from \"react\";\nimport { useThreadViewport } from \"../../context/react/ThreadViewportContext\";\n\nexport const useOnScrollToBottom = (\n callback: (config: { behavior: ScrollBehavior }) => void,\n) => {\n const callbackRef = useCallbackRef(callback);\n const onScrollToBottom = useThreadViewport((vp) => vp.onScrollToBottom);\n\n useEffect(() => {\n return onScrollToBottom(callbackRef);\n }, [onScrollToBottom, callbackRef]);\n};\n"],"mappings":";;;;;;AAMA,MAAaK,uBAAsBC,aAAA;CAAA,MAAAC,IAAAN,EAAA,CAAA;CAGjC,MAAAO,cAAoBN,eAAeI,QAAQ;CAC3C,MAAAG,mBAAyBL,kBAAkBM,KAA2B;CAAE,IAAAC;CAAA,IAAAC;CAAA,IAAAL,EAAA,OAAAC,eAAAD,EAAA,OAAAE,kBAAA;EAE9DE,WACDF,iBAAiBD,WAAW;EAClCI,KAAA,CAACH,kBAAkBD,WAAW;EAACD,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAI;EAAAJ,EAAA,KAAAK;CAAA,OAAA;EAAAD,KAAAJ,EAAA;EAAAK,KAAAL,EAAA;CAAA;CAFlCJ,UAAUQ,IAEPC,EAA+B;AAAC;AARF,SAAAF,MAAAG,IAAA;CAAA,OAIkBA,GAAEJ;AAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSizeHandle.js","names":["c","_c","useCallback","useManagedRef","SizeHandle","useSizeHandle","register","getHeight","$","t0","el","sizeHandle","updateHeight","height","offsetHeight","setHeight","ro","ResizeObserver","observe","disconnect","unregister","callbackRef"],"sources":["../../../src/utils/hooks/useSizeHandle.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\nimport type { SizeHandle } from \"../../context/stores/ThreadViewport\";\n\n/**\n * Hook that creates a ref for tracking element size via a SizeHandle.\n * Automatically sets up ResizeObserver and reports height changes.\n *\n * @param register - Function that returns a SizeHandle (e.g., registerContentInset)\n * @param getHeight - Optional function to compute height (defaults to el.offsetHeight)\n * @returns A ref callback to attach to the element\n */\nexport const useSizeHandle = (\n register: (() => SizeHandle) | null | undefined,\n getHeight?: (el: HTMLElement) => number,\n) => {\n const callbackRef = useCallback(\n (el: HTMLElement) => {\n if (!register) return;\n\n const sizeHandle = register();\n\n const updateHeight = () => {\n const height = getHeight ? getHeight(el) : el.offsetHeight;\n sizeHandle.setHeight(height);\n };\n\n const ro = new ResizeObserver(updateHeight);\n ro.observe(el);\n updateHeight();\n\n return () => {\n ro.disconnect();\n sizeHandle.unregister();\n };\n },\n [register, getHeight],\n );\n\n return useManagedRef(callbackRef);\n};\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAaK,iBAAgBC,UAAAC,cAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,aAAAC,EAAA,OAAAF,UAAA;EAKzBG,MAAAC,OAAA;GACE,IAAI,CAACJ,UAAQ;GAEb,MAAAK,aAAmBL,SAAS;GAE5B,MAAAM,qBAAqB;IACnB,MAAAC,SAAeN,YAAYA,UAAUG,EAAoB,IAAdA,GAAEI;IAC7CH,WAAUI,UAAWF,MAAM;GAAC;GAG9B,MAAAG,KAAW,IAAIC,eAAeL,YAAY;GAC1CI,GAAEE,QAASR,EAAE;GACbE,aAAa;GAAC,aAEP;IACLI,GAAEG,WAAY;IACdR,WAAUS,WAAY;GAAC;EACxB;EACFZ,EAAA,KAAAD;EAAAC,EAAA,KAAAF;EAAAE,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAED,OAEKL,cAAckB,EAAW;AAAC"}
|
|
1
|
+
{"version":3,"file":"useSizeHandle.js","names":["c","_c","useCallback","useManagedRef","SizeHandle","useSizeHandle","register","getHeight","$","t0","el","sizeHandle","updateHeight","height","offsetHeight","setHeight","ro","ResizeObserver","observe","disconnect","unregister","callbackRef","_c","callbackRef"],"sources":["../../../src/utils/hooks/useSizeHandle.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useManagedRef } from \"./useManagedRef\";\nimport type { SizeHandle } from \"../../context/stores/ThreadViewport\";\n\n/**\n * Hook that creates a ref for tracking element size via a SizeHandle.\n * Automatically sets up ResizeObserver and reports height changes.\n *\n * @param register - Function that returns a SizeHandle (e.g., registerContentInset)\n * @param getHeight - Optional function to compute height (defaults to el.offsetHeight)\n * @returns A ref callback to attach to the element\n */\nexport const useSizeHandle = (\n register: (() => SizeHandle) | null | undefined,\n getHeight?: (el: HTMLElement) => number,\n) => {\n const callbackRef = useCallback(\n (el: HTMLElement) => {\n if (!register) return;\n\n const sizeHandle = register();\n\n const updateHeight = () => {\n const height = getHeight ? getHeight(el) : el.offsetHeight;\n sizeHandle.setHeight(height);\n };\n\n const ro = new ResizeObserver(updateHeight);\n ro.observe(el);\n updateHeight();\n\n return () => {\n ro.disconnect();\n sizeHandle.unregister();\n };\n },\n [register, getHeight],\n );\n\n return useManagedRef(callbackRef);\n};\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAaK,iBAAgBC,UAAAC,cAAA;CAAA,MAAAC,IAAAP,EAAA,CAAA;CAAA,IAAAQ;CAAA,IAAAD,EAAA,OAAAD,aAAAC,EAAA,OAAAF,UAAA;EAKzBG,MAAAC,OAAA;GACE,IAAI,CAACJ,UAAQ;GAEb,MAAAK,aAAmBL,SAAS;GAE5B,MAAAM,qBAAqB;IACnB,MAAAC,SAAeN,YAAYA,UAAUG,EAAoB,IAAdA,GAAEI;IAC7CH,WAAUI,UAAWF,MAAM;GAAC;GAG9B,MAAAG,KAAW,IAAIC,eAAeL,YAAY;GAC1CI,GAAEE,QAASR,EAAE;GACbE,aAAa;GAAC,aAEP;IACLI,GAAEG,WAAY;IACdR,WAAUS,WAAY;GAAC;EACxB;EACFZ,EAAA,KAAAD;EAAAC,EAAA,KAAAF;EAAAE,EAAA,KAAAC;CAAA,OAAAA,KAAAD,EAAA;CAED,OAEKL,cAAckB,EAAW;AAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isCompositionKey.d.ts","sourceRoot":"","sources":["../../src/utils/isCompositionKey.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,gBAAgB,GAAI,OAAO,aAAa,YACT,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isCompositionKey.js","names":["isCompositionKey","event","KeyboardEvent","isComposing","keyCode"],"sources":["../../src/utils/isCompositionKey.ts"],"sourcesContent":["// A keydown that ends an IME composition can arrive after compositionend with\n// isComposing false and keyCode 229, as the Enter that commits one does in\n// Safari.\nexport const isCompositionKey = (event: KeyboardEvent) =>\n event.isComposing || event.keyCode === 229;\n"],"mappings":";AAGA,MAAaA,oBAAoBC,UAC/BA,MAAME,eAAeF,MAAMG,YAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SmoothContext.js","names":["c","_c","ComponentType","createContext","FC","forwardRef","PropsWithChildren","useContext","useState","ReadonlyStore","create","UseBoundStore","MessagePartStatus","ToolCallMessagePartStatus","useAui","createContextStoreHook","SmoothContextValue","useSmoothStatus","SmoothContext","makeSmoothContext","initialState","SmoothContextProvider","t0","$","children","t1","Symbol","for","optional","outer","useSmoothContext","aui","t2","part","getState","status","context","t3","withSmoothContextProvider","Component","C","Wrapped","props","ref","displayName","options","Error","useSmoothStatusStore"],"sources":["../../../src/utils/smooth/SmoothContext.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n createContext,\n type FC,\n forwardRef,\n type PropsWithChildren,\n useContext,\n useState,\n} from \"react\";\nimport type { ReadonlyStore } from \"../../context/ReadonlyStore\";\nimport { create, type UseBoundStore } from \"zustand\";\nimport type {\n MessagePartStatus,\n ToolCallMessagePartStatus,\n} from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { createContextStoreHook } from \"../../context/react/utils/createContextStoreHook\";\n\ntype SmoothContextValue = {\n useSmoothStatus: UseBoundStore<\n ReadonlyStore<MessagePartStatus | ToolCallMessagePartStatus>\n >;\n};\n\nconst SmoothContext = createContext<SmoothContextValue | null>(null);\n\nconst makeSmoothContext = (\n initialState: MessagePartStatus | ToolCallMessagePartStatus,\n) => {\n const useSmoothStatus = create(() => initialState);\n return { useSmoothStatus };\n};\n\nexport const SmoothContextProvider: FC<PropsWithChildren> = ({ children }) => {\n const outer = useSmoothContext({ optional: true });\n const aui = useAui();\n\n const [context] = useState(() =>\n makeSmoothContext(aui.part.getState().status),\n );\n\n // do not wrap if there is an outer SmoothContextProvider\n if (outer) return children;\n\n return (\n <SmoothContext.Provider value={context}>{children}</SmoothContext.Provider>\n );\n};\n\nexport const withSmoothContextProvider = <C extends ComponentType<any>>(\n Component: C,\n): C => {\n const Wrapped = forwardRef((props, ref) => {\n return (\n <SmoothContextProvider>\n <Component {...(props as any)} ref={ref} />\n </SmoothContextProvider>\n );\n });\n Wrapped.displayName = Component.displayName;\n return Wrapped as any;\n};\n\nfunction useSmoothContext(options?: {\n optional?: false | undefined;\n}): SmoothContextValue;\nfunction useSmoothContext(options?: {\n optional?: boolean | undefined;\n}): SmoothContextValue | null;\nfunction useSmoothContext(options?: { optional?: boolean | undefined }) {\n const context = useContext(SmoothContext);\n if (!options?.optional && !context)\n throw new Error(\n \"This component must be used within a SmoothContextProvider.\",\n );\n return context;\n}\n\nexport const { useSmoothStatus, useSmoothStatusStore } = createContextStoreHook(\n useSmoothContext,\n \"useSmoothStatus\",\n);\n"],"mappings":";;;;;;;;AA0BA,MAAMkB,gBAAgBf,cAAyC,IAAI;AAEnE,MAAMgB,qBACJC,iBACG;CAEH,OAAO,EAAEH,iBADeP,aAAaU,YAC5BH,EAAgB;AAC3B;AAEA,MAAaI,yBAA+CC,OAAA;CAAA,MAAAC,IAAAtB,EAAA,CAAA;CAAC,MAAA,EAAAuB,aAAAF;CAAY,IAAAG;CAAA,IAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EACxCF,KAAA,EAAAG,UAAY,KAAK;EAACL,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAjD,MAAAM,QAAcC,iBAAiBL,EAAkB;CACjD,MAAAM,MAAYjB,OAAO;CAAE,IAAAkB;CAAA,IAAAT,EAAA,OAAAQ,IAAAE,MAAA;EAEMD,WACzBb,kBAAkBY,IAAGE,KAAKC,SAAU,CAAC,CAAAC,MAAO;EAACZ,EAAA,KAAAQ,IAAAE;EAAAV,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAD/C,MAAA,CAAAa,WAAkB5B,SAASwB,EAE3B;CAGA,IAAIH,OAAK,OAASL;CAAS,IAAAa;CAAA,IAAAd,EAAA,OAAAC,YAAAD,EAAA,OAAAa,SAAA;EAGzBC,KAAA,oBAAA,cAAA,UAAA;GAA+BD,OAAAA;GAAUZ;EAAS,CAAA;EAAyBD,EAAA,KAAAC;EAAAD,EAAA,KAAAa;EAAAb,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAAA,OAA3Ec;AAA2E;AAI/E,MAAaC,6BACXC,cACM;CACN,MAAME,UAAUpC,YAAWqC,OAAAC,QAAA;EAAA,MAAApB,IAAAtB,EAAA,CAAA;EAGL,MAAAqB,KAAAoB;EAAY,IAAAjB;EAAA,IAAAF,EAAA,OAAAoB,OAAApB,EAAA,OAAAD,IAAA;GAD9BG,KAAA,oBAAC,uBAAD,EAAA,UACE,oBAAC,WAAD;IAAU,GAAMH;IAAoBqB;GAAG,CAAA,EADnB,CAAA;GAEEpB,EAAA,KAAAoB;GAAApB,EAAA,KAAAD;GAAAC,EAAA,KAAAE;EAAA,OAAAA,KAAAF,EAAA;EAAA,OAFxBE;CAEwB,CAE3B;CACDgB,QAAQG,cAAcL,UAAUK;CAChC,OAAOH;AACT;AAQA,SAAAX,iBAAAe,SAAA;CACE,MAAAT,UAAgB7B,WAAWW,aAAa;CACxC,IAAI,CAAC2B,SAAOjB,YAAR,CAAuBQ,SACzB,MAAM,IAAIU,MACR,6DACF;CAAE,OACGV;AAAO;AAGhB,MAAa,EAAEnB,iBAAiB8B,yBAAyBhC,uBACvDe,kBACA,iBACF"}
|
|
1
|
+
{"version":3,"file":"SmoothContext.js","names":["c","_c","ComponentType","createContext","FC","forwardRef","PropsWithChildren","useContext","useState","ReadonlyStore","create","UseBoundStore","MessagePartStatus","ToolCallMessagePartStatus","useAui","createContextStoreHook","SmoothContextValue","useSmoothStatus","SmoothContext","makeSmoothContext","initialState","SmoothContextProvider","t0","$","children","t1","Symbol","for","optional","outer","useSmoothContext","aui","t2","part","getState","status","context","t3","withSmoothContextProvider","Component","C","Wrapped","props","ref","displayName","options","Error","useSmoothStatusStore","_c"],"sources":["../../../src/utils/smooth/SmoothContext.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n createContext,\n type FC,\n forwardRef,\n type PropsWithChildren,\n useContext,\n useState,\n} from \"react\";\nimport type { ReadonlyStore } from \"../../context/ReadonlyStore\";\nimport { create, type UseBoundStore } from \"zustand\";\nimport type {\n MessagePartStatus,\n ToolCallMessagePartStatus,\n} from \"@assistant-ui/core\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { createContextStoreHook } from \"../../context/react/utils/createContextStoreHook\";\n\ntype SmoothContextValue = {\n useSmoothStatus: UseBoundStore<\n ReadonlyStore<MessagePartStatus | ToolCallMessagePartStatus>\n >;\n};\n\nconst SmoothContext = createContext<SmoothContextValue | null>(null);\n\nconst makeSmoothContext = (\n initialState: MessagePartStatus | ToolCallMessagePartStatus,\n) => {\n const useSmoothStatus = create(() => initialState);\n return { useSmoothStatus };\n};\n\nexport const SmoothContextProvider: FC<PropsWithChildren> = ({ children }) => {\n const outer = useSmoothContext({ optional: true });\n const aui = useAui();\n\n const [context] = useState(() =>\n makeSmoothContext(aui.part.getState().status),\n );\n\n // do not wrap if there is an outer SmoothContextProvider\n if (outer) return children;\n\n return (\n <SmoothContext.Provider value={context}>{children}</SmoothContext.Provider>\n );\n};\n\nexport const withSmoothContextProvider = <C extends ComponentType<any>>(\n Component: C,\n): C => {\n const Wrapped = forwardRef((props, ref) => {\n return (\n <SmoothContextProvider>\n <Component {...(props as any)} ref={ref} />\n </SmoothContextProvider>\n );\n });\n Wrapped.displayName = Component.displayName;\n return Wrapped as any;\n};\n\nfunction useSmoothContext(options?: {\n optional?: false | undefined;\n}): SmoothContextValue;\nfunction useSmoothContext(options?: {\n optional?: boolean | undefined;\n}): SmoothContextValue | null;\nfunction useSmoothContext(options?: { optional?: boolean | undefined }) {\n const context = useContext(SmoothContext);\n if (!options?.optional && !context)\n throw new Error(\n \"This component must be used within a SmoothContextProvider.\",\n );\n return context;\n}\n\nexport const { useSmoothStatus, useSmoothStatusStore } = createContextStoreHook(\n useSmoothContext,\n \"useSmoothStatus\",\n);\n"],"mappings":";;;;;;;;AA0BA,MAAMkB,gBAAgBf,cAAyC,IAAI;AAEnE,MAAMgB,qBACJC,iBACG;CAEH,OAAO,EAAEH,iBADeP,aAAaU,YAC5BH,EAAgB;AAC3B;AAEA,MAAaI,yBAA+CC,OAAA;CAAA,MAAAC,IAAAtB,EAAA,CAAA;CAAC,MAAA,EAAAuB,aAAAF;CAAY,IAAAG;CAAA,IAAAF,EAAA,OAAAG,OAAAC,IAAA,2BAAA,GAAA;EACxCF,KAAA,EAAAG,UAAY,KAAK;EAACL,EAAA,KAAAE;CAAA,OAAAA,KAAAF,EAAA;CAAjD,MAAAM,QAAcC,iBAAiBL,EAAkB;CACjD,MAAAM,MAAYjB,OAAO;CAAE,IAAAkB;CAAA,IAAAT,EAAA,OAAAQ,IAAAE,MAAA;EAEMD,WACzBb,kBAAkBY,IAAGE,KAAKC,SAAU,CAAC,CAAAC,MAAO;EAACZ,EAAA,KAAAQ,IAAAE;EAAAV,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAD/C,MAAA,CAAAa,WAAkB5B,SAASwB,EAE3B;CAGA,IAAIH,OAAK,OAASL;CAAS,IAAAa;CAAA,IAAAd,EAAA,OAAAC,YAAAD,EAAA,OAAAa,SAAA;EAGzBC,KAAA,oBAAA,cAAA,UAAA;GAA+BD,OAAAA;GAAUZ;EAAS,CAAA;EAAyBD,EAAA,KAAAC;EAAAD,EAAA,KAAAa;EAAAb,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAAA,OAA3Ec;AAA2E;AAI/E,MAAaC,6BACXC,cACM;CACN,MAAME,UAAUpC,YAAWqC,OAAAC,QAAA;EAAA,MAAApB,IAAAtB,EAAA,CAAA;EAGL,MAAAqB,KAAAoB;EAAY,IAAAjB;EAAA,IAAAF,EAAA,OAAAoB,OAAApB,EAAA,OAAAD,IAAA;GAD9BG,KAAA,oBAAC,uBAAD,EAAA,UACE,oBAAC,WAAD;IAAU,GAAMH;IAAoBqB;GAAG,CAAA,EADnB,CAAA;GAEEpB,EAAA,KAAAoB;GAAApB,EAAA,KAAAD;GAAAC,EAAA,KAAAE;EAAA,OAAAA,KAAAF,EAAA;EAAA,OAFxBE;CAEwB,CAE3B;CACDgB,QAAQG,cAAcL,UAAUK;CAChC,OAAOH;AACT;AAQA,SAAAX,iBAAAe,SAAA;CACE,MAAAT,UAAgB7B,WAAWW,aAAa;CACxC,IAAI,CAAC2B,SAAOjB,YAAR,CAAuBQ,SACzB,MAAM,IAAIU,MACR,6DACF;CAAE,OACGV;AAAO;AAGhB,MAAa,EAAEnB,iBAAiB8B,yBAAyBhC,uBACvDe,kBACA,iBACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.24",
|
|
4
4
|
"description": "Open-source TypeScript/React library for building production-grade AI chat experiences",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -55,14 +55,14 @@
|
|
|
55
55
|
],
|
|
56
56
|
"sideEffects": false,
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@assistant-ui/core": "^0.3.
|
|
59
|
-
"@assistant-ui/store": "^0.3.
|
|
60
|
-
"@assistant-ui/tap": "^0.9.
|
|
61
|
-
"assistant-cloud": "^0.2.
|
|
62
|
-
"assistant-stream": "^0.3.
|
|
58
|
+
"@assistant-ui/core": "^0.3.23",
|
|
59
|
+
"@assistant-ui/store": "^0.3.17",
|
|
60
|
+
"@assistant-ui/tap": "^0.9.21",
|
|
61
|
+
"assistant-cloud": "^0.2.5",
|
|
62
|
+
"assistant-stream": "^0.3.47",
|
|
63
63
|
"radix-ui": "^1.6.7",
|
|
64
64
|
"react-textarea-autosize": "^8.5.9",
|
|
65
|
-
"safe-content-frame": "^0.0.
|
|
65
|
+
"safe-content-frame": "^0.0.32",
|
|
66
66
|
"zod": "^4.6.5",
|
|
67
67
|
"zustand": "^5.0.15"
|
|
68
68
|
},
|
|
@@ -81,18 +81,18 @@
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
|
-
"@assistant-ui/vite": "0.0.
|
|
85
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
84
|
+
"@assistant-ui/vite": "0.0.20",
|
|
85
|
+
"@assistant-ui/x-buildutils": "0.0.30",
|
|
86
86
|
"@assistant-ui/x-react18": "0.0.0",
|
|
87
87
|
"@testing-library/react": "^16.3.3",
|
|
88
88
|
"@types/json-schema": "^7.0.15",
|
|
89
|
-
"@types/node": "^26.
|
|
89
|
+
"@types/node": "^26.6.3",
|
|
90
90
|
"@types/react": "^19.3.0",
|
|
91
91
|
"@types/react-dom": "^19.3.0",
|
|
92
92
|
"jsdom": "^30.0.1",
|
|
93
93
|
"react": "^19.3.0",
|
|
94
94
|
"react-dom": "^19.3.0",
|
|
95
|
-
"vitest": "^5.0.
|
|
95
|
+
"vitest": "^5.0.3"
|
|
96
96
|
},
|
|
97
97
|
"publishConfig": {
|
|
98
98
|
"access": "public",
|
|
@@ -133,6 +133,7 @@ const fireKeyDown = (
|
|
|
133
133
|
ctrlKey?: boolean;
|
|
134
134
|
metaKey?: boolean;
|
|
135
135
|
isComposing?: boolean;
|
|
136
|
+
keyCode?: number;
|
|
136
137
|
} = {},
|
|
137
138
|
): KeyboardEvent => {
|
|
138
139
|
const event = new KeyboardEvent("keydown", {
|
|
@@ -143,6 +144,7 @@ const fireKeyDown = (
|
|
|
143
144
|
ctrlKey: opts.ctrlKey ?? false,
|
|
144
145
|
metaKey: opts.metaKey ?? false,
|
|
145
146
|
isComposing: opts.isComposing ?? false,
|
|
147
|
+
keyCode: opts.keyCode ?? 0,
|
|
146
148
|
});
|
|
147
149
|
textarea.dispatchEvent(event);
|
|
148
150
|
return event;
|
|
@@ -441,6 +443,20 @@ describe("ComposerPrimitiveInput", () => {
|
|
|
441
443
|
expect(event.defaultPrevented).toBe(false);
|
|
442
444
|
});
|
|
443
445
|
|
|
446
|
+
it("ignores the Enter that commits an IME composition after compositionend (Safari)", async () => {
|
|
447
|
+
const textarea = await mount();
|
|
448
|
+
|
|
449
|
+
let event!: KeyboardEvent;
|
|
450
|
+
await act(async () => {
|
|
451
|
+
fireCompositionStart(textarea);
|
|
452
|
+
fireCompositionEnd(textarea, "日本語");
|
|
453
|
+
event = fireKeyDown(textarea, { key: "Enter", keyCode: 229 });
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
expect(requestSubmitSpy).not.toHaveBeenCalled();
|
|
457
|
+
expect(event.defaultPrevented).toBe(false);
|
|
458
|
+
});
|
|
459
|
+
|
|
444
460
|
it("submitMode='ctrlEnter' submits on Cmd/Ctrl+Enter but not on plain Enter", async () => {
|
|
445
461
|
const textarea = await mount({ submitMode: "ctrlEnter" });
|
|
446
462
|
|
|
@@ -566,13 +582,14 @@ describe("ComposerPrimitiveInput", () => {
|
|
|
566
582
|
describe("escape behavior", () => {
|
|
567
583
|
const fireEscape = (
|
|
568
584
|
textarea: HTMLTextAreaElement,
|
|
569
|
-
opts: { isComposing?: boolean } = {},
|
|
585
|
+
opts: { isComposing?: boolean; keyCode?: number } = {},
|
|
570
586
|
): KeyboardEvent => {
|
|
571
587
|
const event = new KeyboardEvent("keydown", {
|
|
572
588
|
bubbles: true,
|
|
573
589
|
cancelable: true,
|
|
574
590
|
key: "Escape",
|
|
575
591
|
isComposing: opts.isComposing ?? false,
|
|
592
|
+
keyCode: opts.keyCode ?? 0,
|
|
576
593
|
});
|
|
577
594
|
textarea.dispatchEvent(event);
|
|
578
595
|
escapeKeydownHandler?.(event);
|
|
@@ -604,6 +621,21 @@ describe("ComposerPrimitiveInput", () => {
|
|
|
604
621
|
expect(cancelSpy).not.toHaveBeenCalled();
|
|
605
622
|
expect(event.defaultPrevented).toBe(false);
|
|
606
623
|
});
|
|
624
|
+
|
|
625
|
+
it("ignores the Escape that cancels an IME composition after compositionend (Safari)", async () => {
|
|
626
|
+
composerState.canCancel = true;
|
|
627
|
+
const textarea = await mount();
|
|
628
|
+
|
|
629
|
+
let event!: KeyboardEvent;
|
|
630
|
+
await act(async () => {
|
|
631
|
+
fireCompositionStart(textarea);
|
|
632
|
+
fireCompositionEnd(textarea, "");
|
|
633
|
+
event = fireEscape(textarea, { keyCode: 229 });
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
expect(cancelSpy).not.toHaveBeenCalled();
|
|
637
|
+
expect(event.defaultPrevented).toBe(false);
|
|
638
|
+
});
|
|
607
639
|
});
|
|
608
640
|
|
|
609
641
|
describe("paste attachments", () => {
|
|
@@ -22,6 +22,7 @@ import { useEscapeKeydown } from "radix-ui/internal";
|
|
|
22
22
|
import { useOnScrollToBottom } from "../../utils/hooks/useOnScrollToBottom";
|
|
23
23
|
import { useMediaQuery } from "../../utils/hooks/useMediaQuery";
|
|
24
24
|
import { renderSlot } from "../../utils/Primitive";
|
|
25
|
+
import { isCompositionKey } from "../../utils/isCompositionKey";
|
|
25
26
|
import { useAui } from "@assistant-ui/store";
|
|
26
27
|
import { flushTapSync } from "@assistant-ui/tap";
|
|
27
28
|
import { useComposerInputPluginRegistryOptional } from "./ComposerInputPluginContext";
|
|
@@ -200,7 +201,7 @@ export const ComposerPrimitiveInput = forwardRef<
|
|
|
200
201
|
if (!textareaRef.current?.contains(e.target as Node)) return;
|
|
201
202
|
|
|
202
203
|
// ignore IME composition events
|
|
203
|
-
if (e
|
|
204
|
+
if (isCompositionKey(e)) return;
|
|
204
205
|
|
|
205
206
|
// Let registered plugins (mention, slash command, etc.) handle Escape first
|
|
206
207
|
if (pluginRegistry) {
|
|
@@ -222,7 +223,7 @@ export const ComposerPrimitiveInput = forwardRef<
|
|
|
222
223
|
if (isDisabled) return;
|
|
223
224
|
|
|
224
225
|
// ignore IME composition events
|
|
225
|
-
if (e.nativeEvent
|
|
226
|
+
if (isCompositionKey(e.nativeEvent)) return;
|
|
226
227
|
|
|
227
228
|
// Let registered plugins (mention, slash command, etc.) handle keyboard events first
|
|
228
229
|
if (pluginRegistry) {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
2
|
|
|
3
3
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
type FC,
|
|
8
|
+
type ReactNode,
|
|
9
|
+
type PropsWithChildren,
|
|
10
|
+
} from "react";
|
|
5
11
|
import { describe, expect, it } from "vitest";
|
|
6
12
|
import type { ThreadMessageLike } from "@assistant-ui/core";
|
|
7
13
|
import {
|
|
@@ -63,10 +69,12 @@ const Example = ({
|
|
|
63
69
|
content,
|
|
64
70
|
Message: MessageComponent = Message,
|
|
65
71
|
extra,
|
|
72
|
+
isRunning = false,
|
|
66
73
|
}: {
|
|
67
74
|
content: ThreadMessageLike["content"];
|
|
68
75
|
Message?: FC;
|
|
69
76
|
extra?: ReactNode;
|
|
77
|
+
isRunning?: boolean;
|
|
70
78
|
}) => {
|
|
71
79
|
const messages: ThreadMessageLike[] = [
|
|
72
80
|
{ id: "message", role: "assistant", content },
|
|
@@ -74,6 +82,7 @@ const Example = ({
|
|
|
74
82
|
const runtime = useExternalStoreRuntime({
|
|
75
83
|
messages,
|
|
76
84
|
convertMessage: (message) => message,
|
|
85
|
+
isRunning,
|
|
77
86
|
onNew: async () => {},
|
|
78
87
|
});
|
|
79
88
|
return (
|
|
@@ -88,6 +97,267 @@ const Example = ({
|
|
|
88
97
|
};
|
|
89
98
|
|
|
90
99
|
describe("MessagePrimitive.Unstable_PartsGroupedByParentId", () => {
|
|
100
|
+
it.each(["tool-call", "reasoning"] as const)(
|
|
101
|
+
"keeps wrapper and mounted state when a tool call is appended after %s",
|
|
102
|
+
(firstType) => {
|
|
103
|
+
let mounts = 0;
|
|
104
|
+
const Group = ({ children }: PropsWithChildren) => {
|
|
105
|
+
const [mount] = useState(() => ++mounts);
|
|
106
|
+
return <section data-mount={mount}>{children}</section>;
|
|
107
|
+
};
|
|
108
|
+
const Text = ({ text }: { text: string }) => {
|
|
109
|
+
const [seed] = useState(text);
|
|
110
|
+
return <span>{`${seed}:${text}`}</span>;
|
|
111
|
+
};
|
|
112
|
+
const Tool = ({ argsText }: { argsText: string }) => (
|
|
113
|
+
<Text text={argsText} />
|
|
114
|
+
);
|
|
115
|
+
const Message = partsMessage({
|
|
116
|
+
Reasoning: Text,
|
|
117
|
+
Group,
|
|
118
|
+
tools: { Fallback: Tool },
|
|
119
|
+
});
|
|
120
|
+
const first =
|
|
121
|
+
firstType === "tool-call"
|
|
122
|
+
? {
|
|
123
|
+
type: "tool-call" as const,
|
|
124
|
+
toolCallId: "t2",
|
|
125
|
+
toolName: "task",
|
|
126
|
+
args: {},
|
|
127
|
+
argsText: "draft",
|
|
128
|
+
parentId: "parent",
|
|
129
|
+
}
|
|
130
|
+
: {
|
|
131
|
+
type: "reasoning" as const,
|
|
132
|
+
id: "r1",
|
|
133
|
+
text: "draft",
|
|
134
|
+
parentId: "parent",
|
|
135
|
+
};
|
|
136
|
+
const view = render(
|
|
137
|
+
<Example Message={Message} content={[first]} isRunning />,
|
|
138
|
+
);
|
|
139
|
+
const wrapper = view.container.querySelector("section");
|
|
140
|
+
const streamed =
|
|
141
|
+
first.type === "tool-call"
|
|
142
|
+
? { ...first, argsText: "streamed" }
|
|
143
|
+
: { ...first, text: "streamed" };
|
|
144
|
+
const appended = {
|
|
145
|
+
type: "tool-call" as const,
|
|
146
|
+
toolCallId: "t1",
|
|
147
|
+
toolName: "task",
|
|
148
|
+
args: {},
|
|
149
|
+
argsText: "appended",
|
|
150
|
+
parentId: "parent",
|
|
151
|
+
};
|
|
152
|
+
view.rerender(
|
|
153
|
+
<Example Message={Message} content={[streamed, appended]} isRunning />,
|
|
154
|
+
);
|
|
155
|
+
expect(view.container.querySelector("section")).toBe(wrapper);
|
|
156
|
+
expect(wrapper?.dataset.mount).toBe("1");
|
|
157
|
+
expect(
|
|
158
|
+
Array.from(
|
|
159
|
+
view.container.querySelectorAll("span"),
|
|
160
|
+
(el) => el.textContent,
|
|
161
|
+
),
|
|
162
|
+
).toEqual(["draft:streamed", "appended:appended"]);
|
|
163
|
+
view.rerender(
|
|
164
|
+
<Example
|
|
165
|
+
Message={Message}
|
|
166
|
+
content={[
|
|
167
|
+
streamed,
|
|
168
|
+
{ ...appended, argsText: "updated" },
|
|
169
|
+
{ ...appended, toolCallId: "t0", argsText: "last" },
|
|
170
|
+
]}
|
|
171
|
+
isRunning
|
|
172
|
+
/>,
|
|
173
|
+
);
|
|
174
|
+
expect(view.container.querySelector("section")).toBe(wrapper);
|
|
175
|
+
expect(
|
|
176
|
+
Array.from(
|
|
177
|
+
view.container.querySelectorAll("span"),
|
|
178
|
+
(el) => el.textContent,
|
|
179
|
+
),
|
|
180
|
+
).toEqual(["draft:streamed", "appended:updated", "last:last"]);
|
|
181
|
+
},
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
it("keeps parent names and part ids with key delimiters separate", () => {
|
|
185
|
+
let mounts = 0;
|
|
186
|
+
const Group = ({ children }: PropsWithChildren) => {
|
|
187
|
+
const [mount] = useState(() => ++mounts);
|
|
188
|
+
return <section data-mount={mount}>{children}</section>;
|
|
189
|
+
};
|
|
190
|
+
const Text = ({ text }: { text: string }) => {
|
|
191
|
+
const [seed] = useState(text);
|
|
192
|
+
return <span>{`${seed}:${text}`}</span>;
|
|
193
|
+
};
|
|
194
|
+
const Message = partsMessage({ Text, Group });
|
|
195
|
+
const first = {
|
|
196
|
+
type: "text" as const,
|
|
197
|
+
id: "x-id:text:y",
|
|
198
|
+
text: "first",
|
|
199
|
+
parentId: "a",
|
|
200
|
+
};
|
|
201
|
+
const second = {
|
|
202
|
+
type: "text" as const,
|
|
203
|
+
id: "y",
|
|
204
|
+
text: "second",
|
|
205
|
+
parentId: "a-id:text:x",
|
|
206
|
+
};
|
|
207
|
+
const view = render(
|
|
208
|
+
<Example Message={Message} content={[first, second]} />,
|
|
209
|
+
);
|
|
210
|
+
view.rerender(
|
|
211
|
+
<Example
|
|
212
|
+
Message={Message}
|
|
213
|
+
content={[
|
|
214
|
+
{ ...second, text: "second updated" },
|
|
215
|
+
{ ...first, text: "first updated" },
|
|
216
|
+
]}
|
|
217
|
+
/>,
|
|
218
|
+
);
|
|
219
|
+
expect(
|
|
220
|
+
Array.from(
|
|
221
|
+
view.container.querySelectorAll("section"),
|
|
222
|
+
(el) => el.dataset.mount,
|
|
223
|
+
),
|
|
224
|
+
).toEqual(["2", "1"]);
|
|
225
|
+
expect(
|
|
226
|
+
Array.from(
|
|
227
|
+
view.container.querySelectorAll("span"),
|
|
228
|
+
(el) => el.textContent,
|
|
229
|
+
),
|
|
230
|
+
).toEqual(["second:second updated", "first:first updated"]);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it.each([undefined, "parent"])(
|
|
234
|
+
"keeps wrapper and leaf state when later identified parts swap with parent=%s",
|
|
235
|
+
(parentId) => {
|
|
236
|
+
let mounts = 0;
|
|
237
|
+
const Group = ({ children }: PropsWithChildren) => {
|
|
238
|
+
const [mount] = useState(() => ++mounts);
|
|
239
|
+
return <section data-mount={mount}>{children}</section>;
|
|
240
|
+
};
|
|
241
|
+
const Text = ({ text }: { text: string }) => {
|
|
242
|
+
const [seed] = useState(text);
|
|
243
|
+
return <span>{`${seed}:${text}`}</span>;
|
|
244
|
+
};
|
|
245
|
+
const Message = partsMessage({ Text, Group });
|
|
246
|
+
const first = {
|
|
247
|
+
type: "text" as const,
|
|
248
|
+
id: "p1",
|
|
249
|
+
text: "first",
|
|
250
|
+
...(parentId !== undefined && { parentId }),
|
|
251
|
+
};
|
|
252
|
+
const second = {
|
|
253
|
+
type: "text" as const,
|
|
254
|
+
id: "p2",
|
|
255
|
+
text: "second",
|
|
256
|
+
...(parentId !== undefined && { parentId }),
|
|
257
|
+
};
|
|
258
|
+
const third = {
|
|
259
|
+
...second,
|
|
260
|
+
id: "p3",
|
|
261
|
+
text: "third",
|
|
262
|
+
};
|
|
263
|
+
const view = render(
|
|
264
|
+
<Example Message={Message} content={[first, second, third]} />,
|
|
265
|
+
);
|
|
266
|
+
view.rerender(
|
|
267
|
+
<Example
|
|
268
|
+
Message={Message}
|
|
269
|
+
content={[
|
|
270
|
+
{ ...first, text: "first updated" },
|
|
271
|
+
{ ...third, text: "third updated" },
|
|
272
|
+
{ ...second, text: "second updated" },
|
|
273
|
+
]}
|
|
274
|
+
/>,
|
|
275
|
+
);
|
|
276
|
+
expect(
|
|
277
|
+
Array.from(
|
|
278
|
+
view.container.querySelectorAll("span"),
|
|
279
|
+
(el) => el.textContent,
|
|
280
|
+
),
|
|
281
|
+
).toEqual([
|
|
282
|
+
"first:first updated",
|
|
283
|
+
"third:third updated",
|
|
284
|
+
"second:second updated",
|
|
285
|
+
]);
|
|
286
|
+
expect(
|
|
287
|
+
Array.from(
|
|
288
|
+
view.container.querySelectorAll("section"),
|
|
289
|
+
(el) => el.dataset.mount,
|
|
290
|
+
),
|
|
291
|
+
).toEqual(parentId === undefined ? ["1", "3", "2"] : ["1"]);
|
|
292
|
+
},
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
it("keys unidentified groups by index and keeps parent names separate", () => {
|
|
296
|
+
let mounts = 0;
|
|
297
|
+
const Group = ({
|
|
298
|
+
groupKey,
|
|
299
|
+
children,
|
|
300
|
+
}: PropsWithChildren<{ groupKey: string | undefined }>) => {
|
|
301
|
+
const [mount] = useState(() => ++mounts);
|
|
302
|
+
return (
|
|
303
|
+
<section data-mount={mount} data-parent={groupKey}>
|
|
304
|
+
{children}
|
|
305
|
+
</section>
|
|
306
|
+
);
|
|
307
|
+
};
|
|
308
|
+
const Message = partsMessage({ Group });
|
|
309
|
+
const parts = [
|
|
310
|
+
{ type: "text" as const, text: "first" },
|
|
311
|
+
{ type: "text" as const, text: "second" },
|
|
312
|
+
];
|
|
313
|
+
const view = render(<Example Message={Message} content={parts} />);
|
|
314
|
+
view.rerender(
|
|
315
|
+
<Example
|
|
316
|
+
Message={Message}
|
|
317
|
+
content={[
|
|
318
|
+
{ type: "text", text: "parented", parentId: "ungrouped" },
|
|
319
|
+
...parts,
|
|
320
|
+
]}
|
|
321
|
+
/>,
|
|
322
|
+
);
|
|
323
|
+
expect(
|
|
324
|
+
Array.from(
|
|
325
|
+
view.container.querySelectorAll("section"),
|
|
326
|
+
(el) => el.dataset.mount,
|
|
327
|
+
),
|
|
328
|
+
).toEqual(["3", "2", "4"]);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("keeps a text seed while streaming and resets it for a replacement id", () => {
|
|
332
|
+
const SeededText = ({ text }: { text: string }) => {
|
|
333
|
+
const [seed] = useState(text);
|
|
334
|
+
return <span>{`${seed}:${text}`}</span>;
|
|
335
|
+
};
|
|
336
|
+
const Message = partsMessage({ Text: SeededText });
|
|
337
|
+
const view = render(
|
|
338
|
+
<Example
|
|
339
|
+
Message={Message}
|
|
340
|
+
content={[{ type: "text", id: "p1", text: "old" }]}
|
|
341
|
+
/>,
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
view.rerender(
|
|
345
|
+
<Example
|
|
346
|
+
Message={Message}
|
|
347
|
+
content={[{ type: "text", id: "p1", text: "old streamed" }]}
|
|
348
|
+
/>,
|
|
349
|
+
);
|
|
350
|
+
expect(view.container.textContent).toBe("old:old streamed");
|
|
351
|
+
|
|
352
|
+
view.rerender(
|
|
353
|
+
<Example
|
|
354
|
+
Message={Message}
|
|
355
|
+
content={[{ type: "text", id: "p2", text: "new" }]}
|
|
356
|
+
/>,
|
|
357
|
+
);
|
|
358
|
+
expect(view.container.textContent).toBe("new:new");
|
|
359
|
+
});
|
|
360
|
+
|
|
91
361
|
it("keeps parent IDs separate from ungrouped parts across content updates", () => {
|
|
92
362
|
const { rerender } = render(
|
|
93
363
|
<Example
|
|
@@ -27,6 +27,7 @@ import type {
|
|
|
27
27
|
} from "@assistant-ui/core/react";
|
|
28
28
|
import { MessagePartPrimitiveInProgress } from "../messagePart/MessagePartInProgress";
|
|
29
29
|
import type { MessagePartStatus } from "@assistant-ui/core";
|
|
30
|
+
import { getMessagePartKeys } from "@assistant-ui/core/internal";
|
|
30
31
|
|
|
31
32
|
type MessagePartGroup = {
|
|
32
33
|
groupKey: string | undefined;
|
|
@@ -73,15 +74,16 @@ const groupMessagePartsByParentId: GroupingFunction = (
|
|
|
73
74
|
|
|
74
75
|
const useMessagePartsGrouped = (
|
|
75
76
|
groupingFunction: GroupingFunction,
|
|
76
|
-
): MessagePartGroup[] => {
|
|
77
|
+
): { groups: MessagePartGroup[]; partKeys: string[] } => {
|
|
77
78
|
const parts = useAuiState((s) => s.message.parts);
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
const groups = useMemo(() => {
|
|
80
81
|
if (parts.length === 0) {
|
|
81
82
|
return [];
|
|
82
83
|
}
|
|
83
84
|
return groupingFunction(parts);
|
|
84
85
|
}, [parts, groupingFunction]);
|
|
86
|
+
return { groups, partKeys: getMessagePartKeys(parts) };
|
|
85
87
|
};
|
|
86
88
|
|
|
87
89
|
export namespace MessagePrimitiveUnstable_PartsGrouped {
|
|
@@ -463,7 +465,8 @@ export const MessagePrimitiveUnstable_PartsGrouped: FC<
|
|
|
463
465
|
MessagePrimitiveUnstable_PartsGrouped.Props
|
|
464
466
|
> = ({ groupingFunction, components }) => {
|
|
465
467
|
const contentLength = useAuiState((s) => s.message.parts.length);
|
|
466
|
-
const messageGroups =
|
|
468
|
+
const { groups: messageGroups, partKeys } =
|
|
469
|
+
useMessagePartsGrouped(groupingFunction);
|
|
467
470
|
|
|
468
471
|
const partsElements = useMemo(() => {
|
|
469
472
|
if (contentLength === 0) {
|
|
@@ -472,16 +475,21 @@ export const MessagePrimitiveUnstable_PartsGrouped: FC<
|
|
|
472
475
|
|
|
473
476
|
return messageGroups.map((group, groupIndex) => {
|
|
474
477
|
const GroupComponent = components?.Group ?? defaultComponents.Group;
|
|
478
|
+
const identity = partKeys[group.indices[0]!];
|
|
475
479
|
|
|
476
480
|
return (
|
|
477
481
|
<GroupComponent
|
|
478
|
-
key={
|
|
482
|
+
key={JSON.stringify([
|
|
483
|
+
"group",
|
|
484
|
+
group.groupKey ?? null,
|
|
485
|
+
identity?.includes(":") ? `id:${identity}` : groupIndex,
|
|
486
|
+
])}
|
|
479
487
|
groupKey={group.groupKey}
|
|
480
488
|
indices={group.indices}
|
|
481
489
|
>
|
|
482
490
|
{group.indices.map((partIndex) => (
|
|
483
491
|
<MessagePart
|
|
484
|
-
key={partIndex}
|
|
492
|
+
key={partKeys[partIndex]}
|
|
485
493
|
partIndex={partIndex}
|
|
486
494
|
components={components}
|
|
487
495
|
/>
|
|
@@ -489,7 +497,7 @@ export const MessagePrimitiveUnstable_PartsGrouped: FC<
|
|
|
489
497
|
</GroupComponent>
|
|
490
498
|
);
|
|
491
499
|
});
|
|
492
|
-
}, [messageGroups, components, contentLength]);
|
|
500
|
+
}, [messageGroups, components, contentLength, partKeys]);
|
|
493
501
|
|
|
494
502
|
return <>{partsElements}</>;
|
|
495
503
|
};
|