@assistant-ui/react 0.15.26 → 0.15.28

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 (37) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/mcp-apps/app-frame.js +2 -2
  4. package/dist/mcp-apps/app-frame.js.map +1 -1
  5. package/dist/mcp-apps/bridge.d.ts.map +1 -1
  6. package/dist/mcp-apps/bridge.js +28 -3
  7. package/dist/mcp-apps/bridge.js.map +1 -1
  8. package/dist/mcp-apps/index.d.ts +1 -1
  9. package/dist/mcp-apps/index.d.ts.map +1 -1
  10. package/dist/mcp-apps/types.d.ts +18 -1
  11. package/dist/mcp-apps/types.d.ts.map +1 -1
  12. package/dist/mcp-apps/types.js.map +1 -1
  13. package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
  14. package/dist/primitives/composer/ComposerAttachmentDropzone.js +14 -12
  15. package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
  16. package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
  17. package/dist/primitives/composer/ComposerRoot.js +63 -55
  18. package/dist/primitives/composer/ComposerRoot.js.map +1 -1
  19. package/dist/unstable/useComposerInput.d.ts +2 -0
  20. package/dist/unstable/useComposerInput.d.ts.map +1 -1
  21. package/dist/unstable/useComposerInput.js +2 -0
  22. package/dist/unstable/useComposerInput.js.map +1 -1
  23. package/package.json +7 -7
  24. package/src/index.ts +1 -0
  25. package/src/mcp-apps/McpAppRenderer.test.tsx +7 -2
  26. package/src/mcp-apps/app-frame.test.tsx +64 -6
  27. package/src/mcp-apps/app-frame.tsx +2 -2
  28. package/src/mcp-apps/bridge.test.ts +154 -0
  29. package/src/mcp-apps/bridge.ts +51 -9
  30. package/src/mcp-apps/index.ts +1 -0
  31. package/src/mcp-apps/types.ts +15 -1
  32. package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +32 -1
  33. package/src/primitives/composer/ComposerAttachmentDropzone.tsx +14 -12
  34. package/src/primitives/composer/ComposerDisabledThread.test.tsx +121 -0
  35. package/src/primitives/composer/ComposerRoot.test.tsx +21 -1
  36. package/src/primitives/composer/ComposerRoot.tsx +8 -1
  37. package/src/unstable/useComposerInput.ts +2 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ComposerAttachmentDropzone.js","names":["forwardRef","useCallback","useEffect","useState","ReactElement","isValidElement","composeEventHandlers","Slot","React","useAui","renderSlot","ComposerPrimitiveAttachmentDropzone","Element","HTMLDivElement","Props","HTMLAttributes","asChild","render","disabled","children","rest","ref","isDragging","setIsDragging","aui","handleDragEnterCapture","e","DragEvent","dataTransfer","types","includes","preventDefault","thread","getState","capabilities","attachments","dropEffect","handleDragOverCapture","handleDragLeaveCapture","next","relatedTarget","Node","currentTarget","contains","handleDrop","files","Array","from","length","Promise","all","map","file","composer","addAttachment","mergedProps","onDragEnterCapture","onDragOverCapture","onDragLeaveCapture","onDropCapture","Comp","Root","displayName"],"sources":["../../../src/primitives/composer/ComposerAttachmentDropzone.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useState,\n type ReactElement,\n isValidElement,\n} from \"react\";\n\nimport { composeEventHandlers } from \"radix-ui/internal\";\nimport { Slot } from \"radix-ui\";\nimport type React from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { renderSlot } from \"../../utils/Primitive\";\n\nexport namespace ComposerPrimitiveAttachmentDropzone {\n export type Element = HTMLDivElement;\n export type Props = React.HTMLAttributes<HTMLDivElement> & {\n asChild?: boolean | undefined;\n render?: ReactElement | undefined;\n disabled?: boolean | undefined;\n };\n}\n\nexport const ComposerPrimitiveAttachmentDropzone = forwardRef<\n HTMLDivElement,\n ComposerPrimitiveAttachmentDropzone.Props\n>(({ disabled, asChild = false, render, children, ...rest }, ref) => {\n const [isDragging, setIsDragging] = useState(false);\n const aui = useAui();\n\n useEffect(() => {\n if (!disabled) return;\n // Disabled handlers cannot clear the latch, so reset it when the enabled period ends.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsDragging(false);\n }, [disabled]);\n\n // An unprevented file drop navigates the tab to the file, so file drags are\n // claimed via preventDefault even when the runtime does not support attachments.\n const handleDragEnterCapture = useCallback(\n (e: React.DragEvent) => {\n if (!e.dataTransfer.types.includes(\"Files\")) return;\n e.preventDefault();\n if (disabled || !aui.thread.getState().capabilities.attachments) {\n e.dataTransfer.dropEffect = \"none\";\n return;\n }\n setIsDragging(true);\n },\n [disabled, aui],\n );\n\n const handleDragOverCapture = useCallback(\n (e: React.DragEvent) => {\n if (!e.dataTransfer.types.includes(\"Files\")) return;\n e.preventDefault();\n if (disabled || !aui.thread.getState().capabilities.attachments) {\n e.dataTransfer.dropEffect = \"none\";\n return;\n }\n if (!isDragging) setIsDragging(true);\n },\n [disabled, isDragging, aui],\n );\n\n const handleDragLeaveCapture = useCallback(\n (e: React.DragEvent) => {\n if (disabled) return;\n e.preventDefault();\n const next = e.relatedTarget as Node | null;\n if (next && e.currentTarget.contains(next)) {\n return;\n }\n setIsDragging(false);\n },\n [disabled],\n );\n\n const handleDrop = useCallback(\n async (e: React.DragEvent) => {\n setIsDragging(false);\n if (!e.dataTransfer.types.includes(\"Files\")) return;\n e.preventDefault();\n if (disabled) return;\n const files = Array.from(e.dataTransfer.files);\n if (!aui.thread.getState().capabilities.attachments || files.length === 0)\n return;\n await Promise.all(\n files.map(async (file) => {\n try {\n await aui.composer.addAttachment(file);\n } catch {\n // The composer runtime emits composer.attachmentAddError before rejecting;\n // the readonly and empty-thread stubs only throw.\n }\n }),\n );\n },\n [disabled, aui],\n );\n\n const mergedProps = {\n ...(isDragging && !disabled ? { \"data-dragging\": \"true\" } : null),\n ...rest,\n onDragEnterCapture: composeEventHandlers(\n rest.onDragEnterCapture,\n handleDragEnterCapture,\n ),\n onDragOverCapture: composeEventHandlers(\n rest.onDragOverCapture,\n handleDragOverCapture,\n ),\n onDragLeaveCapture: composeEventHandlers(\n rest.onDragLeaveCapture,\n handleDragLeaveCapture,\n ),\n onDropCapture: composeEventHandlers(rest.onDropCapture, handleDrop),\n ref,\n };\n\n if (render && isValidElement(render)) {\n return renderSlot(render, children, mergedProps);\n }\n\n const Comp = asChild ? Slot.Root : \"div\";\n return <Comp {...mergedProps}>{children}</Comp>;\n});\n\nComposerPrimitiveAttachmentDropzone.displayName =\n \"ComposerPrimitive.AttachmentDropzone\";\n"],"mappings":";;;;;;;;AA0BA,MAAaW,sCAAsCX,YAGhD,EAAEkB,UAAUF,UAAU,OAAOC,QAAQE,UAAU,GAAGC,QAAQC,QAAQ;CACnE,MAAM,CAACC,YAAYC,iBAAiBpB,SAAS,KAAK;CAClD,MAAMqB,MAAMf,OAAO;CAEnBP,gBAAgB;EACd,IAAI,CAACgB,UAAU;EAGfK,cAAc,KAAK;CACrB,GAAG,CAACL,QAAQ,CAAC;CAIb,MAAMO,yBAAyBxB,aAC5ByB,MAAuB;EACtB,IAAI,CAACA,EAAEE,aAAaC,MAAMC,SAAS,OAAO,GAAG;EAC7CJ,EAAEK,eAAe;EACjB,IAAIb,YAAY,CAACM,IAAIQ,OAAOC,SAAS,CAAC,CAACC,aAAaC,aAAa;GAC/DT,EAAEE,aAAaQ,aAAa;GAC5B;EACF;EACAb,cAAc,IAAI;CACpB,GACA,CAACL,UAAUM,GAAG,CAChB;CAEA,MAAMa,wBAAwBpC,aAC3ByB,QAAuB;EACtB,IAAI,CAACA,IAAEE,aAAaC,MAAMC,SAAS,OAAO,GAAG;EAC7CJ,IAAEK,eAAe;EACjB,IAAIb,YAAY,CAACM,IAAIQ,OAAOC,SAAS,CAAC,CAACC,aAAaC,aAAa;GAC/DT,IAAEE,aAAaQ,aAAa;GAC5B;EACF;EACA,IAAI,CAACd,YAAYC,cAAc,IAAI;CACrC,GACA;EAACL;EAAUI;EAAYE;CAAG,CAC5B;CAEA,MAAMc,yBAAyBrC,aAC5ByB,QAAuB;EACtB,IAAIR,UAAU;EACdQ,IAAEK,eAAe;EACjB,MAAMQ,OAAOb,IAAEc;EACf,IAAID,QAAQb,IAAEgB,cAAcC,SAASJ,IAAI,GACvC;EAEFhB,cAAc,KAAK;CACrB,GACA,CAACL,QAAQ,CACX;CAEA,MAAM0B,aAAa3C,YACjB,OAAOyB,QAAuB;EAC5BH,cAAc,KAAK;EACnB,IAAI,CAACG,IAAEE,aAAaC,MAAMC,SAAS,OAAO,GAAG;EAC7CJ,IAAEK,eAAe;EACjB,IAAIb,UAAU;EACd,MAAM2B,QAAQC,MAAMC,KAAKrB,IAAEE,aAAaiB,KAAK;EAC7C,IAAI,CAACrB,IAAIQ,OAAOC,SAAS,CAAC,CAACC,aAAaC,eAAeU,MAAMG,WAAW,GACtE;EACF,MAAMC,QAAQC,IACZL,MAAMM,IAAI,OAAOC,SAAS;GACxB,IAAI;IACF,MAAM5B,IAAI6B,SAASC,cAAcF,IAAI;GACvC,QAAQ,CAEN;EAEJ,CAAC,CACH;CACF,GACA,CAAClC,UAAUM,GAAG,CAChB;CAEA,MAAM+B,cAAc;EAClB,GAAIjC,cAAc,CAACJ,WAAW,EAAE,iBAAiB,OAAO,IAAI;EAC5D,GAAGE;EACHoC,oBAAoBlD,qBAClBc,KAAKoC,oBACL/B,sBACF;EACAgC,mBAAmBnD,qBACjBc,KAAKqC,mBACLpB,qBACF;EACAqB,oBAAoBpD,qBAClBc,KAAKsC,oBACLpB,sBACF;EACAqB,eAAerD,qBAAqBc,KAAKuC,eAAef,UAAU;EAClEvB;CACF;CAEA,IAAIJ,UAAUZ,eAAeY,MAAM,GACjC,OAAOP,WAAWO,QAAQE,UAAUoC,WAAW;CAGjD,MAAMK,OAAO5C,UAAUT,KAAKsD,OAAO;CACnC,OAAO,oBAAC,MAAD;EAAM,GAAIN;EAAcpC;CAAe,CAAA;AAChD,CAAC;AAEDR,oCAAoCmD,cAClC"}
1
+ {"version":3,"file":"ComposerAttachmentDropzone.js","names":["forwardRef","useCallback","useEffect","useState","ReactElement","isValidElement","composeEventHandlers","Slot","React","useAui","useAuiState","renderSlot","ComposerPrimitiveAttachmentDropzone","Element","HTMLDivElement","Props","HTMLAttributes","asChild","render","disabled","children","rest","ref","isDragging","setIsDragging","aui","disabledByThread","s","thread","isDisabled","handleDragEnterCapture","e","DragEvent","dataTransfer","types","includes","preventDefault","getState","capabilities","attachments","dropEffect","handleDragOverCapture","handleDragLeaveCapture","next","relatedTarget","Node","currentTarget","contains","handleDrop","files","Array","from","length","Promise","all","map","file","composer","addAttachment","mergedProps","onDragEnterCapture","onDragOverCapture","onDragLeaveCapture","onDropCapture","Comp","Root","displayName"],"sources":["../../../src/primitives/composer/ComposerAttachmentDropzone.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useState,\n type ReactElement,\n isValidElement,\n} from \"react\";\n\nimport { composeEventHandlers } from \"radix-ui/internal\";\nimport { Slot } from \"radix-ui\";\nimport type React from \"react\";\nimport { useAui, useAuiState } from \"@assistant-ui/store\";\nimport { renderSlot } from \"../../utils/Primitive\";\n\nexport namespace ComposerPrimitiveAttachmentDropzone {\n export type Element = HTMLDivElement;\n export type Props = React.HTMLAttributes<HTMLDivElement> & {\n asChild?: boolean | undefined;\n render?: ReactElement | undefined;\n disabled?: boolean | undefined;\n };\n}\n\nexport const ComposerPrimitiveAttachmentDropzone = forwardRef<\n HTMLDivElement,\n ComposerPrimitiveAttachmentDropzone.Props\n>(({ disabled, asChild = false, render, children, ...rest }, ref) => {\n const [isDragging, setIsDragging] = useState(false);\n const aui = useAui();\n const disabledByThread = useAuiState((s) => s.thread.isDisabled);\n const isDisabled = disabled || disabledByThread;\n\n useEffect(() => {\n if (!isDisabled) return;\n // Disabled handlers cannot clear the latch, so reset it when the enabled period ends.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsDragging(false);\n }, [isDisabled]);\n\n // An unprevented file drop navigates the tab to the file, so file drags are\n // claimed via preventDefault even when the runtime does not support attachments.\n const handleDragEnterCapture = useCallback(\n (e: React.DragEvent) => {\n if (!e.dataTransfer.types.includes(\"Files\")) return;\n e.preventDefault();\n if (isDisabled || !aui.thread.getState().capabilities.attachments) {\n e.dataTransfer.dropEffect = \"none\";\n return;\n }\n setIsDragging(true);\n },\n [isDisabled, aui],\n );\n\n const handleDragOverCapture = useCallback(\n (e: React.DragEvent) => {\n if (!e.dataTransfer.types.includes(\"Files\")) return;\n e.preventDefault();\n if (isDisabled || !aui.thread.getState().capabilities.attachments) {\n e.dataTransfer.dropEffect = \"none\";\n return;\n }\n if (!isDragging) setIsDragging(true);\n },\n [isDisabled, isDragging, aui],\n );\n\n const handleDragLeaveCapture = useCallback(\n (e: React.DragEvent) => {\n if (isDisabled) return;\n e.preventDefault();\n const next = e.relatedTarget as Node | null;\n if (next && e.currentTarget.contains(next)) {\n return;\n }\n setIsDragging(false);\n },\n [isDisabled],\n );\n\n const handleDrop = useCallback(\n async (e: React.DragEvent) => {\n setIsDragging(false);\n if (!e.dataTransfer.types.includes(\"Files\")) return;\n e.preventDefault();\n if (isDisabled) return;\n const files = Array.from(e.dataTransfer.files);\n if (!aui.thread.getState().capabilities.attachments || files.length === 0)\n return;\n await Promise.all(\n files.map(async (file) => {\n try {\n await aui.composer.addAttachment(file);\n } catch {\n // The composer runtime emits composer.attachmentAddError before rejecting;\n // the readonly and empty-thread stubs only throw.\n }\n }),\n );\n },\n [isDisabled, aui],\n );\n\n const mergedProps = {\n ...(isDragging && !isDisabled ? { \"data-dragging\": \"true\" } : null),\n ...rest,\n onDragEnterCapture: composeEventHandlers(\n rest.onDragEnterCapture,\n handleDragEnterCapture,\n ),\n onDragOverCapture: composeEventHandlers(\n rest.onDragOverCapture,\n handleDragOverCapture,\n ),\n onDragLeaveCapture: composeEventHandlers(\n rest.onDragLeaveCapture,\n handleDragLeaveCapture,\n ),\n onDropCapture: composeEventHandlers(rest.onDropCapture, handleDrop),\n ref,\n };\n\n if (render && isValidElement(render)) {\n return renderSlot(render, children, mergedProps);\n }\n\n const Comp = asChild ? Slot.Root : \"div\";\n return <Comp {...mergedProps}>{children}</Comp>;\n});\n\nComposerPrimitiveAttachmentDropzone.displayName =\n \"ComposerPrimitive.AttachmentDropzone\";\n"],"mappings":";;;;;;;;AA0BA,MAAaY,sCAAsCZ,YAGhD,EAAEmB,UAAUF,UAAU,OAAOC,QAAQE,UAAU,GAAGC,QAAQC,QAAQ;CACnE,MAAM,CAACC,YAAYC,iBAAiBrB,SAAS,KAAK;CAClD,MAAMsB,MAAMhB,OAAO;CACnB,MAAMiB,mBAAmBhB,aAAaiB,MAAMA,EAAEC,OAAOC,UAAU;CAC/D,MAAMA,aAAaV,YAAYO;CAE/BxB,gBAAgB;EACd,IAAI,CAAC2B,YAAY;EAGjBL,cAAc,KAAK;CACrB,GAAG,CAACK,UAAU,CAAC;CAIf,MAAMC,yBAAyB7B,aAC5B8B,MAAuB;EACtB,IAAI,CAACA,EAAEE,aAAaC,MAAMC,SAAS,OAAO,GAAG;EAC7CJ,EAAEK,eAAe;EACjB,IAAIP,cAAc,CAACJ,IAAIG,OAAOS,SAAS,CAAC,CAACC,aAAaC,aAAa;GACjER,EAAEE,aAAaO,aAAa;GAC5B;EACF;EACAhB,cAAc,IAAI;CACpB,GACA,CAACK,YAAYJ,GAAG,CAClB;CAEA,MAAMgB,wBAAwBxC,aAC3B8B,QAAuB;EACtB,IAAI,CAACA,IAAEE,aAAaC,MAAMC,SAAS,OAAO,GAAG;EAC7CJ,IAAEK,eAAe;EACjB,IAAIP,cAAc,CAACJ,IAAIG,OAAOS,SAAS,CAAC,CAACC,aAAaC,aAAa;GACjER,IAAEE,aAAaO,aAAa;GAC5B;EACF;EACA,IAAI,CAACjB,YAAYC,cAAc,IAAI;CACrC,GACA;EAACK;EAAYN;EAAYE;CAAG,CAC9B;CAEA,MAAMiB,yBAAyBzC,aAC5B8B,QAAuB;EACtB,IAAIF,YAAY;EAChBE,IAAEK,eAAe;EACjB,MAAMO,OAAOZ,IAAEa;EACf,IAAID,QAAQZ,IAAEe,cAAcC,SAASJ,IAAI,GACvC;EAEFnB,cAAc,KAAK;CACrB,GACA,CAACK,UAAU,CACb;CAEA,MAAMmB,aAAa/C,YACjB,OAAO8B,QAAuB;EAC5BP,cAAc,KAAK;EACnB,IAAI,CAACO,IAAEE,aAAaC,MAAMC,SAAS,OAAO,GAAG;EAC7CJ,IAAEK,eAAe;EACjB,IAAIP,YAAY;EAChB,MAAMoB,QAAQC,MAAMC,KAAKpB,IAAEE,aAAagB,KAAK;EAC7C,IAAI,CAACxB,IAAIG,OAAOS,SAAS,CAAC,CAACC,aAAaC,eAAeU,MAAMG,WAAW,GACtE;EACF,MAAMC,QAAQC,IACZL,MAAMM,IAAI,OAAOC,SAAS;GACxB,IAAI;IACF,MAAM/B,IAAIgC,SAASC,cAAcF,IAAI;GACvC,QAAQ,CAEN;EAEJ,CAAC,CACH;CACF,GACA,CAAC3B,YAAYJ,GAAG,CAClB;CAEA,MAAMkC,cAAc;EAClB,GAAIpC,cAAc,CAACM,aAAa,EAAE,iBAAiB,OAAO,IAAI;EAC9D,GAAGR;EACHuC,oBAAoBtD,qBAClBe,KAAKuC,oBACL9B,sBACF;EACA+B,mBAAmBvD,qBACjBe,KAAKwC,mBACLpB,qBACF;EACAqB,oBAAoBxD,qBAClBe,KAAKyC,oBACLpB,sBACF;EACAqB,eAAezD,qBAAqBe,KAAK0C,eAAef,UAAU;EAClE1B;CACF;CAEA,IAAIJ,UAAUb,eAAea,MAAM,GACjC,OAAOP,WAAWO,QAAQE,UAAUuC,WAAW;CAGjD,MAAMK,OAAO/C,UAAUV,KAAK0D,OAAO;CACnC,OAAO,oBAAC,MAAD;EAAM,GAAIN;EAAcvC;CAAe,CAAA;AAChD,CAAC;AAEDR,oCAAoCsD,cAClC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ComposerRoot.d.ts","sourceRoot":"","sources":["../../../src/primitives/composer/ComposerRoot.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,iCAA8B;AAClD,OAAO,EACL,KAAK,YAAY,EAGjB,KAAK,wBAAwB,EAI9B,MAAM,OAAO,CAAC;AA0Bf,yBAAiB,qBAAqB,CAAC;IACrC,KAAY,OAAO,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1D;;;OAGG;IACH,KAAY,KAAK,GAAG,wBAAwB,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG;QACpE;;;;;;;WAOG;QACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAC/B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,qBAAqB;;;;;IAhC9B;;;;;;;OAOG;cACO,OAAO,GAAG,SAAS;mDAkF/B,CAAC"}
1
+ {"version":3,"file":"ComposerRoot.d.ts","sourceRoot":"","sources":["../../../src/primitives/composer/ComposerRoot.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,iCAA8B;AAClD,OAAO,EACL,KAAK,YAAY,EAGjB,KAAK,wBAAwB,EAK9B,MAAM,OAAO,CAAC;AA0Bf,yBAAiB,qBAAqB,CAAC;IACrC,KAAY,OAAO,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1D;;;OAGG;IACH,KAAY,KAAK,GAAG,wBAAwB,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,GAAG;QACpE;;;;;;;WAOG;QACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;KAC/B,CAAC;CACH;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,qBAAqB;;;;;IAnC9B;;;;;;;OAOG;cACO,OAAO,GAAG,SAAS;mDAwF/B,CAAC"}
@@ -4,7 +4,7 @@ import { useComposerSend } from "./ComposerSend.js";
4
4
  import { ComposerCompactContext } from "./ComposerCompactContext.js";
5
5
  import { COMPOSER_INPUT_SELECTOR, CONTENT_EDITABLE_SELECTOR } from "./composerInputSelector.js";
6
6
  import { c } from "@assistant-ui/tap/react-shim/compiler-runtime";
7
- import { forwardRef, useState } from "@assistant-ui/tap/react-shim";
7
+ import { forwardRef, useState, version } from "@assistant-ui/tap/react-shim";
8
8
  import { useAuiState } from "@assistant-ui/store";
9
9
  import { jsx } from "react/jsx-runtime";
10
10
  import { composeEventHandlers } from "radix-ui/internal";
@@ -25,6 +25,7 @@ const INTERACTIVE_ELEMENT_SELECTOR = [
25
25
  CONTENT_EDITABLE_SELECTOR,
26
26
  "[tabindex]"
27
27
  ].join(", ");
28
+ const isReact18 = version.startsWith("18.");
28
29
  /**
29
30
  * The root form container for message composition.
30
31
  *
@@ -46,96 +47,103 @@ const INTERACTIVE_ELEMENT_SELECTOR = [
46
47
  * ```
47
48
  */
48
49
  const ComposerPrimitiveRoot = forwardRef((t0, forwardedRef) => {
49
- const $ = c(25);
50
+ const $ = c(27);
50
51
  let compact;
52
+ let inert;
51
53
  let onMouseDown;
52
54
  let onSubmit;
53
55
  let rest;
54
56
  if ($[0] !== t0) {
55
- ({onSubmit, onMouseDown, compact, ...rest} = t0);
57
+ ({onSubmit, onMouseDown, compact, inert, ...rest} = t0);
56
58
  $[0] = t0;
57
59
  $[1] = compact;
58
- $[2] = onMouseDown;
59
- $[3] = onSubmit;
60
- $[4] = rest;
60
+ $[2] = inert;
61
+ $[3] = onMouseDown;
62
+ $[4] = onSubmit;
63
+ $[5] = rest;
61
64
  } else {
62
65
  compact = $[1];
63
- onMouseDown = $[2];
64
- onSubmit = $[3];
65
- rest = $[4];
66
+ inert = $[2];
67
+ onMouseDown = $[3];
68
+ onSubmit = $[4];
69
+ rest = $[5];
66
70
  }
67
71
  const send = useComposerSend();
68
72
  const composerType = useAuiState(_temp);
69
73
  const [multiline, setMultiline] = useState(false);
70
74
  let t1;
71
- if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
75
+ if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
72
76
  t1 = { setMultiline };
73
- $[5] = t1;
74
- } else t1 = $[5];
77
+ $[6] = t1;
78
+ } else t1 = $[6];
75
79
  const compactContext = t1;
76
80
  let t2;
77
- if ($[6] !== compact) {
81
+ if ($[7] !== compact) {
78
82
  t2 = (s_0) => compact ? s_0.composer.attachments.length === 0 && s_0.composer.quote == null && s_0.composer.queue.length === 0 && s_0.composer.dictation == null && !s_0.composer.text.includes("\n") : false;
79
- $[6] = compact;
80
- $[7] = t2;
81
- } else t2 = $[7];
83
+ $[7] = compact;
84
+ $[8] = t2;
85
+ } else t2 = $[8];
82
86
  const isCompact = useAuiState(t2) && !multiline;
87
+ const inertAttribute = isReact18 && typeof inert === "boolean" ? inert ? "" : void 0 : inert;
83
88
  let t3;
84
- if ($[8] !== send) {
89
+ if ($[9] !== send) {
85
90
  t3 = (e) => {
86
91
  e.preventDefault();
87
92
  if (!send) return;
88
93
  send();
89
94
  };
90
- $[8] = send;
91
- $[9] = t3;
92
- } else t3 = $[9];
95
+ $[9] = send;
96
+ $[10] = t3;
97
+ } else t3 = $[10];
93
98
  const handleSubmit = t3;
94
99
  const handleMouseDown = _temp2;
95
100
  const t4 = compact ? compactContext : null;
96
- const t5 = isCompact ? "" : void 0;
97
- let t6;
98
- if ($[10] !== handleSubmit || $[11] !== onSubmit) {
99
- t6 = composeEventHandlers(onSubmit, handleSubmit);
100
- $[10] = handleSubmit;
101
- $[11] = onSubmit;
102
- $[12] = t6;
103
- } else t6 = $[12];
101
+ const t5 = inertAttribute;
102
+ const t6 = isCompact ? "" : void 0;
104
103
  let t7;
105
- if ($[13] !== onMouseDown) {
106
- t7 = composeEventHandlers(onMouseDown, handleMouseDown);
107
- $[13] = onMouseDown;
108
- $[14] = t7;
109
- } else t7 = $[14];
104
+ if ($[11] !== handleSubmit || $[12] !== onSubmit) {
105
+ t7 = composeEventHandlers(onSubmit, handleSubmit);
106
+ $[11] = handleSubmit;
107
+ $[12] = onSubmit;
108
+ $[13] = t7;
109
+ } else t7 = $[13];
110
110
  let t8;
111
- if ($[15] !== composerType || $[16] !== forwardedRef || $[17] !== rest || $[18] !== t5 || $[19] !== t6 || $[20] !== t7) {
112
- t8 = /* @__PURE__ */ jsx(Primitive$1.form, {
111
+ if ($[14] !== onMouseDown) {
112
+ t8 = composeEventHandlers(onMouseDown, handleMouseDown);
113
+ $[14] = onMouseDown;
114
+ $[15] = t8;
115
+ } else t8 = $[15];
116
+ let t9;
117
+ if ($[16] !== composerType || $[17] !== forwardedRef || $[18] !== rest || $[19] !== t5 || $[20] !== t6 || $[21] !== t7 || $[22] !== t8) {
118
+ t9 = /* @__PURE__ */ jsx(Primitive$1.form, {
113
119
  ...rest,
120
+ inert: t5,
114
121
  "data-aui-composer-type": composerType,
115
- "data-compact": t5,
122
+ "data-compact": t6,
116
123
  ref: forwardedRef,
117
- onSubmit: t6,
118
- onMouseDown: t7
124
+ onSubmit: t7,
125
+ onMouseDown: t8
119
126
  });
120
- $[15] = composerType;
121
- $[16] = forwardedRef;
122
- $[17] = rest;
123
- $[18] = t5;
124
- $[19] = t6;
125
- $[20] = t7;
126
- $[21] = t8;
127
- } else t8 = $[21];
128
- let t9;
129
- if ($[22] !== t4 || $[23] !== t8) {
130
- t9 = /* @__PURE__ */ jsx(ComposerCompactContext.Provider, {
127
+ $[16] = composerType;
128
+ $[17] = forwardedRef;
129
+ $[18] = rest;
130
+ $[19] = t5;
131
+ $[20] = t6;
132
+ $[21] = t7;
133
+ $[22] = t8;
134
+ $[23] = t9;
135
+ } else t9 = $[23];
136
+ let t10;
137
+ if ($[24] !== t4 || $[25] !== t9) {
138
+ t10 = /* @__PURE__ */ jsx(ComposerCompactContext.Provider, {
131
139
  value: t4,
132
- children: t8
140
+ children: t9
133
141
  });
134
- $[22] = t4;
135
- $[23] = t8;
136
- $[24] = t9;
137
- } else t9 = $[24];
138
- return t9;
142
+ $[24] = t4;
143
+ $[25] = t9;
144
+ $[26] = t10;
145
+ } else t10 = $[26];
146
+ return t10;
139
147
  });
140
148
  ComposerPrimitiveRoot.displayName = "ComposerPrimitive.Root";
141
149
  function _temp(s) {
@@ -1 +1 @@
1
- {"version":3,"file":"ComposerRoot.js","names":["c","_c","composeEventHandlers","useAuiState","Primitive","ComponentRef","FormEvent","forwardRef","ComponentPropsWithoutRef","MouseEvent","useMemo","useState","useComposerSend","ComposerCompactContext","COMPOSER_INPUT_SELECTOR","CONTENT_EDITABLE_SELECTOR","INTERACTIVE_ELEMENT_SELECTOR","join","ComposerPrimitiveRoot","Element","form","Props","compact","t0","forwardedRef","$","onMouseDown","onSubmit","rest","send","composerType","_temp","multiline","setMultiline","t1","Symbol","for","compactContext","t2","s_0","s","composer","attachments","length","quote","queue","dictation","text","includes","stateAllowsCompact","isCompact","t3","e","preventDefault","handleSubmit","handleMouseDown","_temp2","t4","t5","undefined","t6","t7","t8","t9","displayName","type","e_0","button","target","currentTarget","contains","interactive","closest","input","querySelector","focus","_c","Primitive"],"sources":["../../../src/primitives/composer/ComposerRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { composeEventHandlers } from \"radix-ui/internal\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n type FormEvent,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEvent,\n useMemo,\n useState,\n} from \"react\";\nimport { useComposerSend } from \"./ComposerSend\";\nimport { ComposerCompactContext } from \"./ComposerCompactContext\";\nimport {\n COMPOSER_INPUT_SELECTOR,\n CONTENT_EDITABLE_SELECTOR,\n} from \"./composerInputSelector\";\n\n// Keeps tabindex=\"-1\": roving-tabindex widgets click-focus items at -1.\nconst INTERACTIVE_ELEMENT_SELECTOR = [\n \"a\",\n \"audio[controls]\",\n \"button\",\n \"details\",\n \"embed\",\n \"iframe\",\n \"input\",\n \"label\",\n \"select\",\n \"summary\",\n \"textarea\",\n \"video[controls]\",\n CONTENT_EDITABLE_SELECTOR,\n \"[tabindex]\",\n].join(\", \");\n\nexport namespace ComposerPrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.form>;\n /**\n * Props for the ComposerPrimitive.Root component.\n * Accepts all standard form element props.\n */\n export type Props = ComponentPropsWithoutRef<typeof Primitive.form> & {\n /**\n * Opt the composer into compact mode. While the input holds at most a single\n * line of text and there are no attachments, quote, queued messages, or\n * active dictation, the root renders a `data-compact` attribute so styles\n * can collapse the composer into a single row. It expands automatically\n * when any of those conditions stop holding.\n * @default false\n */\n compact?: boolean | undefined;\n };\n}\n\n/**\n * The root form container for message composition.\n *\n * This component provides a form wrapper that handles message submission when the form\n * is submitted (e.g., via Enter key or submit button). It automatically prevents the\n * default form submission and triggers the composer's send functionality.\n *\n * Clicking blank form space focuses the composer input (the first textarea or\n * contenteditable inside the form). Text selection cannot start from blank\n * areas; descendants that need native mousedown defaults can call\n * `stopPropagation()` in their own `onMouseDown`.\n *\n * @example\n * ```tsx\n * <ComposerPrimitive.Root>\n * <ComposerPrimitive.Input placeholder=\"Type your message...\" />\n * <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>\n * </ComposerPrimitive.Root>\n * ```\n */\nexport const ComposerPrimitiveRoot = forwardRef<\n ComposerPrimitiveRoot.Element,\n ComposerPrimitiveRoot.Props\n>(({ onSubmit, onMouseDown, compact, ...rest }, forwardedRef) => {\n const send = useComposerSend();\n const composerType = useAuiState((s) => s.composer.type);\n\n const [multiline, setMultiline] = useState(false);\n const compactContext = useMemo(() => ({ setMultiline }), []);\n const stateAllowsCompact = useAuiState((s) =>\n compact\n ? s.composer.attachments.length === 0 &&\n s.composer.quote == null &&\n s.composer.queue.length === 0 &&\n s.composer.dictation == null &&\n !s.composer.text.includes(\"\\n\")\n : false,\n );\n const isCompact = stateAllowsCompact && !multiline;\n\n const handleSubmit = (e: FormEvent) => {\n e.preventDefault();\n\n if (!send) return;\n send();\n };\n\n const handleMouseDown = (e: MouseEvent<HTMLFormElement>) => {\n if (e.button !== 0) return;\n const target = e.target;\n // Portaled descendants (e.g. a dialog overlay) propagate mousedown through\n // the React tree while their DOM nodes live outside the form.\n if (!(target instanceof Element) || !e.currentTarget.contains(target))\n return;\n // Bound the ancestor walk to the form: hosts like AssistantModal render\n // the composer inside a wrapper carrying tabindex=\"-1\" (Radix FocusScope).\n const interactive = target.closest(INTERACTIVE_ELEMENT_SELECTOR);\n if (interactive && e.currentTarget.contains(interactive)) return;\n const input = e.currentTarget.querySelector<HTMLElement>(\n COMPOSER_INPUT_SELECTOR,\n );\n if (!input) return;\n e.preventDefault();\n input.focus();\n };\n\n return (\n <ComposerCompactContext.Provider value={compact ? compactContext : null}>\n <Primitive.form\n {...rest}\n data-aui-composer-type={composerType}\n data-compact={isCompact ? \"\" : undefined}\n ref={forwardedRef}\n onSubmit={composeEventHandlers(onSubmit, handleSubmit)}\n onMouseDown={composeEventHandlers(onMouseDown, handleMouseDown)}\n />\n </ComposerCompactContext.Provider>\n );\n});\n\nComposerPrimitiveRoot.displayName = \"ComposerPrimitive.Root\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"ComposerRoot.js","names":["c","_c","composeEventHandlers","useAuiState","Primitive","ComponentRef","FormEvent","forwardRef","ComponentPropsWithoutRef","MouseEvent","useMemo","useState","version","useComposerSend","ComposerCompactContext","COMPOSER_INPUT_SELECTOR","CONTENT_EDITABLE_SELECTOR","INTERACTIVE_ELEMENT_SELECTOR","join","ComposerPrimitiveRoot","Element","form","Props","compact","isReact18","startsWith","t0","forwardedRef","$","inert","onMouseDown","onSubmit","rest","send","composerType","_temp","multiline","setMultiline","t1","Symbol","for","compactContext","t2","s_0","s","composer","attachments","length","quote","queue","dictation","text","includes","stateAllowsCompact","isCompact","inertAttribute","undefined","t3","e","preventDefault","handleSubmit","handleMouseDown","_temp2","t4","t5","t6","t7","t8","t9","t10","displayName","type","e_0","button","target","currentTarget","contains","interactive","closest","input","querySelector","focus","_c","Primitive"],"sources":["../../../src/primitives/composer/ComposerRoot.tsx"],"sourcesContent":["\"use client\";\n\nimport { composeEventHandlers } from \"radix-ui/internal\";\nimport { useAuiState } from \"@assistant-ui/store\";\nimport { Primitive } from \"../../utils/Primitive\";\nimport {\n type ComponentRef,\n type FormEvent,\n forwardRef,\n type ComponentPropsWithoutRef,\n type MouseEvent,\n useMemo,\n useState,\n version,\n} from \"react\";\nimport { useComposerSend } from \"./ComposerSend\";\nimport { ComposerCompactContext } from \"./ComposerCompactContext\";\nimport {\n COMPOSER_INPUT_SELECTOR,\n CONTENT_EDITABLE_SELECTOR,\n} from \"./composerInputSelector\";\n\n// Keeps tabindex=\"-1\": roving-tabindex widgets click-focus items at -1.\nconst INTERACTIVE_ELEMENT_SELECTOR = [\n \"a\",\n \"audio[controls]\",\n \"button\",\n \"details\",\n \"embed\",\n \"iframe\",\n \"input\",\n \"label\",\n \"select\",\n \"summary\",\n \"textarea\",\n \"video[controls]\",\n CONTENT_EDITABLE_SELECTOR,\n \"[tabindex]\",\n].join(\", \");\n\nexport namespace ComposerPrimitiveRoot {\n export type Element = ComponentRef<typeof Primitive.form>;\n /**\n * Props for the ComposerPrimitive.Root component.\n * Accepts all standard form element props.\n */\n export type Props = ComponentPropsWithoutRef<typeof Primitive.form> & {\n /**\n * Opt the composer into compact mode. While the input holds at most a single\n * line of text and there are no attachments, quote, queued messages, or\n * active dictation, the root renders a `data-compact` attribute so styles\n * can collapse the composer into a single row. It expands automatically\n * when any of those conditions stop holding.\n * @default false\n */\n compact?: boolean | undefined;\n };\n}\n\n// React 18 drops a boolean `inert`, so it receives the attribute's string form.\nconst isReact18 = version.startsWith(\"18.\");\n\n/**\n * The root form container for message composition.\n *\n * This component provides a form wrapper that handles message submission when the form\n * is submitted (e.g., via Enter key or submit button). It automatically prevents the\n * default form submission and triggers the composer's send functionality.\n *\n * Clicking blank form space focuses the composer input (the first textarea or\n * contenteditable inside the form). Text selection cannot start from blank\n * areas; descendants that need native mousedown defaults can call\n * `stopPropagation()` in their own `onMouseDown`.\n *\n * @example\n * ```tsx\n * <ComposerPrimitive.Root>\n * <ComposerPrimitive.Input placeholder=\"Type your message...\" />\n * <ComposerPrimitive.Send>Send</ComposerPrimitive.Send>\n * </ComposerPrimitive.Root>\n * ```\n */\nexport const ComposerPrimitiveRoot = forwardRef<\n ComposerPrimitiveRoot.Element,\n ComposerPrimitiveRoot.Props\n>(({ onSubmit, onMouseDown, compact, inert, ...rest }, forwardedRef) => {\n const send = useComposerSend();\n const composerType = useAuiState((s) => s.composer.type);\n\n const [multiline, setMultiline] = useState(false);\n const compactContext = useMemo(() => ({ setMultiline }), []);\n const stateAllowsCompact = useAuiState((s) =>\n compact\n ? s.composer.attachments.length === 0 &&\n s.composer.quote == null &&\n s.composer.queue.length === 0 &&\n s.composer.dictation == null &&\n !s.composer.text.includes(\"\\n\")\n : false,\n );\n const isCompact = stateAllowsCompact && !multiline;\n const inertAttribute =\n isReact18 && typeof inert === \"boolean\" ? (inert ? \"\" : undefined) : inert;\n\n const handleSubmit = (e: FormEvent) => {\n e.preventDefault();\n\n if (!send) return;\n send();\n };\n\n const handleMouseDown = (e: MouseEvent<HTMLFormElement>) => {\n if (e.button !== 0) return;\n const target = e.target;\n // Portaled descendants (e.g. a dialog overlay) propagate mousedown through\n // the React tree while their DOM nodes live outside the form.\n if (!(target instanceof Element) || !e.currentTarget.contains(target))\n return;\n // Bound the ancestor walk to the form: hosts like AssistantModal render\n // the composer inside a wrapper carrying tabindex=\"-1\" (Radix FocusScope).\n const interactive = target.closest(INTERACTIVE_ELEMENT_SELECTOR);\n if (interactive && e.currentTarget.contains(interactive)) return;\n const input = e.currentTarget.querySelector<HTMLElement>(\n COMPOSER_INPUT_SELECTOR,\n );\n if (!input) return;\n e.preventDefault();\n input.focus();\n };\n\n return (\n <ComposerCompactContext.Provider value={compact ? compactContext : null}>\n <Primitive.form\n {...rest}\n inert={inertAttribute as boolean | undefined}\n data-aui-composer-type={composerType}\n data-compact={isCompact ? \"\" : undefined}\n ref={forwardedRef}\n onSubmit={composeEventHandlers(onSubmit, handleSubmit)}\n onMouseDown={composeEventHandlers(onMouseDown, handleMouseDown)}\n />\n </ComposerCompactContext.Provider>\n );\n});\n\nComposerPrimitiveRoot.displayName = \"ComposerPrimitive.Root\";\n"],"mappings":"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"}
@@ -54,6 +54,8 @@ export type Unstable_ComposerInput = {
54
54
  * disabled={isDisabled}
55
55
  * onChange={(e) => setText(e.target.value)}
56
56
  * onKeyDown={(e) => {
57
+ * if (e.nativeEvent.isComposing || e.keyCode === 229) return;
58
+ *
57
59
  * if (e.key === "Enter" && !e.shiftKey && canSend) {
58
60
  * e.preventDefault();
59
61
  * send();
@@ -1 +1 @@
1
- {"version":3,"file":"useComposerInput.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInput.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,KAAK,uBAAuB,EAI7B,wDAAqD;AAEtD,+GAA+G;AAC/G,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,sBAAsB,GAAG;IACnC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,CAAC,EAAE,gCAAgC,GACzC,sBAAsB,CA0BxB;AAED,+GAA+G;AAC/G,MAAM,MAAM,gCAAgC,GAAG,uBAAuB,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mCAAmC,IAAI,gCAAgC,CAEtF"}
1
+ {"version":3,"file":"useComposerInput.d.ts","sourceRoot":"","sources":["../../src/unstable/useComposerInput.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,KAAK,uBAAuB,EAI7B,wDAAqD;AAEtD,+GAA+G;AAC/G,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,+GAA+G;AAC/G,MAAM,MAAM,sBAAsB,GAAG;IACnC,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;OAGG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC1C;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,CAAC,EAAE,gCAAgC,GACzC,sBAAsB,CA0BxB;AAED,+GAA+G;AAC/G,MAAM,MAAM,gCAAgC,GAAG,uBAAuB,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mCAAmC,IAAI,gCAAgC,CAEtF"}
@@ -23,6 +23,8 @@ import { flushTapSync } from "@assistant-ui/tap";
23
23
  * disabled={isDisabled}
24
24
  * onChange={(e) => setText(e.target.value)}
25
25
  * onKeyDown={(e) => {
26
+ * if (e.nativeEvent.isComposing || e.keyCode === 229) return;
27
+ *
26
28
  * if (e.key === "Enter" && !e.shiftKey && canSend) {
27
29
  * e.preventDefault();
28
30
  * send();
@@ -1 +1 @@
1
- {"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing; the controlled value stays in\n * sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n * Headless bridge to the composer's text value and send action, for building a\n * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,\n * not a second input: it does not own keyboard behavior, autosize, IME or\n * contentEditable sync, paste/drop attachments, focus management, or rich-text\n * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for\n * trigger-popover combobox semantics.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();\n * <textarea\n * value={value}\n * disabled={isDisabled}\n * onChange={(e) => setText(e.target.value)}\n * onKeyDown={(e) => {\n * if (e.key === \"Enter\" && !e.shiftKey && canSend) {\n * e.preventDefault();\n * send();\n * }\n * }}\n * />\n * ```\n */\nexport function unstable_useComposerInput(\n options?: Unstable_UseComposerInputOptions,\n): Unstable_ComposerInput {\n const aui = useAui();\n const value = useComposerInputValue();\n const isDisabled = useComposerInputDisabled(options?.disabled);\n\n const setText = useCallback(\n (text: string) => {\n if (!aui.composer.getState().isEditing) return;\n flushTapSync(() => {\n aui.composer.setText(text);\n });\n },\n [aui],\n );\n\n const { send: rawSend, disabled: sendDisabled } = useComposerSend();\n const canSend = !sendDisabled && !isDisabled;\n const send = useCallback(\n (sendOptions?: ComposerSendOptions) => {\n if (!canSend) return;\n rawSend(sendOptions);\n },\n [canSend, rawSend],\n );\n\n return { value, setText, send, isDisabled, canSend };\n}\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;\n\n/**\n * ARIA combobox attributes for the focused element (typically the composer\n * input) describing the open trigger popover, per the WAI-ARIA editable\n * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or\n * when no popover is open. Spread these last so they take precedence over any\n * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const aria = unstable_useTriggerPopoverAriaProps();\n * <textarea {...aria} />\n * ```\n */\nexport function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps {\n return useTriggerPopoverAriaProps();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8EA,SAAgBmB,0BACdH,SACwB;CACxB,MAAMI,MAAMnB,OAAO;CACnB,MAAMW,QAAQL,sBAAsB;CACpC,MAAMU,aAAaX,yBAAyBU,SAASN,QAAQ;CAE7D,MAAMG,UAAUb,aACbc,SAAiB;EAChB,IAAI,CAACM,IAAIC,SAASC,SAAS,CAAC,CAACC,WAAW;EACxCrB,mBAAmB;GACjBkB,IAAIC,SAASR,QAAQC,IAAI;EAC3B,CAAC;CACH,GACA,CAACM,GAAG,CACN;CAEA,MAAM,EAAEL,MAAMS,SAASd,UAAUe,iBAAiBtB,gBAAgB;CAClE,MAAMe,UAAU,CAACO,gBAAgB,CAACR;CASlC,OAAO;EAAEL;EAAOC;EAASE,MARZf,aACV0B,gBAAsC;GACrC,IAAI,CAACR,SAAS;GACdM,QAAQE,WAAW;EACrB,GACA,CAACR,SAASM,OAAO,CAGMT;EAAME;EAAYC;CAAQ;AACrD;;;;;;;;;;;;;;;;AAoBA,SAAgBU,sCAAwE;CACtF,OAAOpB,2BAA2B;AACpC"}
1
+ {"version":3,"file":"useComposerInput.js","names":["useCallback","useAui","flushTapSync","useComposerSend","ComposerSendOptions","TriggerPopoverAriaProps","useComposerInputDisabled","useComposerInputValue","useTriggerPopoverAriaProps","Unstable_UseComposerInputOptions","disabled","Unstable_ComposerInput","value","setText","text","send","options","isDisabled","canSend","unstable_useComposerInput","aui","composer","getState","isEditing","rawSend","sendDisabled","sendOptions","Unstable_TriggerPopoverAriaProps","unstable_useTriggerPopoverAriaProps"],"sources":["../../src/unstable/useComposerInput.ts"],"sourcesContent":["\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useAui } from \"@assistant-ui/store\";\nimport { flushTapSync } from \"@assistant-ui/tap\";\nimport { useComposerSend } from \"@assistant-ui/core/react\";\nimport type { ComposerSendOptions } from \"@assistant-ui/core/store\";\nimport {\n type TriggerPopoverAriaProps,\n useComposerInputDisabled,\n useComposerInputValue,\n useTriggerPopoverAriaProps,\n} from \"../primitives/composer/useComposerInputState\";\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_UseComposerInputOptions = {\n /**\n * Disables the input in addition to the composer's own disabled sources\n * (thread disabled, active dictation). When disabled, `isDisabled` is `true`\n * and `canSend` is `false`.\n */\n disabled?: boolean | undefined;\n};\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_ComposerInput = {\n /** Current composer text, or `\"\"` when the composer is not editing. */\n value: string;\n /**\n * Writes `text` into the composer, mirroring `ComposerPrimitive.Input`:\n * a no-op unless the composer is editing; the controlled value stays in\n * sync within the same tick.\n */\n setText(text: string): void;\n /**\n * Sends the current message when `canSend` is `true`; otherwise a no-op.\n * Accepts the same options as the composer send action.\n */\n send(options?: ComposerSendOptions): void;\n /**\n * Whether the input is disabled, combining the `disabled` option with the\n * composer's own disabled sources (thread disabled, active dictation).\n */\n isDisabled: boolean;\n /**\n * Whether a send is currently available. Matches `ComposerPrimitive.Send`\n * gating (non-empty editing composer, not running without queue support) and\n * is additionally `false` while `isDisabled`.\n */\n canSend: boolean;\n};\n\n/**\n * Headless bridge to the composer's text value and send action, for building a\n * custom composer input without `ComposerPrimitive.Input`. It is a thin bridge,\n * not a second input: it does not own keyboard behavior, autosize, IME or\n * contentEditable sync, paste/drop attachments, focus management, or rich-text\n * state. Spread `unstable_useTriggerPopoverAriaProps()` onto your element for\n * trigger-popover combobox semantics.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const { value, setText, send, isDisabled, canSend } = unstable_useComposerInput();\n * <textarea\n * value={value}\n * disabled={isDisabled}\n * onChange={(e) => setText(e.target.value)}\n * onKeyDown={(e) => {\n * if (e.nativeEvent.isComposing || e.keyCode === 229) return;\n *\n * if (e.key === \"Enter\" && !e.shiftKey && canSend) {\n * e.preventDefault();\n * send();\n * }\n * }}\n * />\n * ```\n */\nexport function unstable_useComposerInput(\n options?: Unstable_UseComposerInputOptions,\n): Unstable_ComposerInput {\n const aui = useAui();\n const value = useComposerInputValue();\n const isDisabled = useComposerInputDisabled(options?.disabled);\n\n const setText = useCallback(\n (text: string) => {\n if (!aui.composer.getState().isEditing) return;\n flushTapSync(() => {\n aui.composer.setText(text);\n });\n },\n [aui],\n );\n\n const { send: rawSend, disabled: sendDisabled } = useComposerSend();\n const canSend = !sendDisabled && !isDisabled;\n const send = useCallback(\n (sendOptions?: ComposerSendOptions) => {\n if (!canSend) return;\n rawSend(sendOptions);\n },\n [canSend, rawSend],\n );\n\n return { value, setText, send, isDisabled, canSend };\n}\n\n/** @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release. */\nexport type Unstable_TriggerPopoverAriaProps = TriggerPopoverAriaProps;\n\n/**\n * ARIA combobox attributes for the focused element (typically the composer\n * input) describing the open trigger popover, per the WAI-ARIA editable\n * combobox pattern. Returns an empty object outside a `TriggerPopoverRoot` or\n * when no popover is open. Spread these last so they take precedence over any\n * matching ARIA props you set yourself, mirroring `ComposerPrimitive.Input`.\n *\n * @deprecated Experimental since 2026-06-23. Not scheduled for removal; the API may change in any release.\n *\n * @example\n * ```tsx\n * const aria = unstable_useTriggerPopoverAriaProps();\n * <textarea {...aria} />\n * ```\n */\nexport function unstable_useTriggerPopoverAriaProps(): Unstable_TriggerPopoverAriaProps {\n return useTriggerPopoverAriaProps();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,SAAgBmB,0BACdH,SACwB;CACxB,MAAMI,MAAMnB,OAAO;CACnB,MAAMW,QAAQL,sBAAsB;CACpC,MAAMU,aAAaX,yBAAyBU,SAASN,QAAQ;CAE7D,MAAMG,UAAUb,aACbc,SAAiB;EAChB,IAAI,CAACM,IAAIC,SAASC,SAAS,CAAC,CAACC,WAAW;EACxCrB,mBAAmB;GACjBkB,IAAIC,SAASR,QAAQC,IAAI;EAC3B,CAAC;CACH,GACA,CAACM,GAAG,CACN;CAEA,MAAM,EAAEL,MAAMS,SAASd,UAAUe,iBAAiBtB,gBAAgB;CAClE,MAAMe,UAAU,CAACO,gBAAgB,CAACR;CASlC,OAAO;EAAEL;EAAOC;EAASE,MARZf,aACV0B,gBAAsC;GACrC,IAAI,CAACR,SAAS;GACdM,QAAQE,WAAW;EACrB,GACA,CAACR,SAASM,OAAO,CAGMT;EAAME;EAAYC;CAAQ;AACrD;;;;;;;;;;;;;;;;AAoBA,SAAgBU,sCAAwE;CACtF,OAAOpB,2BAA2B;AACpC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/react",
3
- "version": "0.15.26",
3
+ "version": "0.15.28",
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.25",
59
- "@assistant-ui/store": "^0.3.18",
60
- "@assistant-ui/tap": "^0.9.22",
61
- "assistant-cloud": "^0.2.6",
62
- "assistant-stream": "^0.3.49",
58
+ "@assistant-ui/core": "^0.3.27",
59
+ "@assistant-ui/store": "^0.3.19",
60
+ "@assistant-ui/tap": "^0.9.23",
61
+ "assistant-cloud": "^0.2.7",
62
+ "assistant-stream": "^0.3.50",
63
63
  "radix-ui": "^1.7.0",
64
64
  "react-textarea-autosize": "^8.5.9",
65
- "safe-content-frame": "^0.0.33",
65
+ "safe-content-frame": "^0.0.34",
66
66
  "zod": "^4.6.5",
67
67
  "zustand": "^5.0.15"
68
68
  },
package/src/index.ts CHANGED
@@ -586,6 +586,7 @@ export type {
586
586
  McpAppsHost,
587
587
  McpAppsRemoteHostOptions,
588
588
  McpAppToolCallParams,
589
+ McpAppInitializeParams,
589
590
  McpAppBridgeHandlers,
590
591
  ToolCallMessagePartMcpMetadata,
591
592
  } from "./mcp-apps";
@@ -11,6 +11,7 @@ import type {
11
11
  McpAppBridgeHandlers,
12
12
  McpAppFrameProps,
13
13
  McpAppHostContext,
14
+ McpAppInitializeParams,
14
15
  McpAppsHost,
15
16
  McpAppsRemoteHostOptions,
16
17
  } from "./types";
@@ -229,8 +230,12 @@ describe("McpAppRenderer", () => {
229
230
  await waitFor(() => expect(framePropsCalls().length).toBeGreaterThan(0));
230
231
  const handlers = framePropsCalls().at(-1)?.handlers;
231
232
  expect(handlers?.requestDisplayMode).toBeDefined();
232
- handlers?.onInitialized?.();
233
+ const params: McpAppInitializeParams = {
234
+ appCapabilities: { availableDisplayModes: ["inline", "fullscreen"] },
235
+ };
236
+ handlers?.onInitialized?.(params);
233
237
  expect(onInitialized).toHaveBeenCalledOnce();
238
+ expect(onInitialized).toHaveBeenCalledWith(params);
234
239
  });
235
240
 
236
241
  it("leaves mounted parts alone when renderer options are unchanged", async () => {
@@ -759,7 +764,7 @@ describe("McpAppRenderer", () => {
759
764
  await handlers.updateModelContext?.({ text: "context" });
760
765
  await handlers.openLink?.({ url: "https://example.com" });
761
766
  await handlers.sendMessage?.({ text: "hello" });
762
- handlers.onInitialized?.();
767
+ handlers.onInitialized?.({});
763
768
  await handlers.callTool?.({ name: "search" });
764
769
  await handlers.readResource?.({ uri: "ui://resource" });
765
770
  await handlers.listResources?.();
@@ -8,7 +8,11 @@ import type {
8
8
  } from "../sandbox-host/SandboxHost";
9
9
  import type { CreateMcpAppBridgeOptions, McpAppBridge } from "./bridge";
10
10
  import type * as BridgeModule from "./bridge";
11
- import { MCP_APP_MIME_TYPE, type McpAppHostContext } from "./types";
11
+ import {
12
+ MCP_APP_MIME_TYPE,
13
+ type McpAppHostContext,
14
+ type McpAppInitializeParams,
15
+ } from "./types";
12
16
 
13
17
  const { sandboxHostMock, createMcpAppBridgeMock } = vi.hoisted(() => ({
14
18
  sandboxHostMock: vi.fn(),
@@ -272,6 +276,60 @@ describe("McpAppFrame", () => {
272
276
  }
273
277
  });
274
278
 
279
+ it("forwards initialization metadata to the live handler", () => {
280
+ let createBridge: SandboxHostProps["createBridge"] | null = null;
281
+ sandboxHostMock.mockImplementation((props: SandboxHostProps) => {
282
+ createBridge ??= props.createBridge;
283
+ return null;
284
+ });
285
+ const bridge: McpAppBridge = {
286
+ onMessage: vi.fn(),
287
+ dispose: vi.fn(),
288
+ notifyToolInput: vi.fn(),
289
+ notifyToolResult: vi.fn(),
290
+ notifyHostContextChanged: vi.fn(),
291
+ };
292
+ createMcpAppBridgeMock.mockReturnValue(bridge);
293
+ const onInitialized = vi.fn();
294
+ const view = (handler: typeof onInitialized) => (
295
+ <McpAppFrame
296
+ app={{ resourceUri: "ui://example/widget" }}
297
+ resource={{
298
+ uri: "ui://example/widget",
299
+ mimeType: MCP_APP_MIME_TYPE,
300
+ html: "",
301
+ }}
302
+ handlers={{ onInitialized: handler }}
303
+ />
304
+ );
305
+ const rendered = render(view(onInitialized));
306
+
307
+ const sandboxBridge = createBridge!(
308
+ {
309
+ iframe: document.createElement("iframe"),
310
+ origin: "https://widget.example",
311
+ sendMessage: vi.fn(),
312
+ },
313
+ { setHeight: vi.fn() },
314
+ );
315
+ const options = createMcpAppBridgeMock.mock
316
+ .calls[0]![0] as CreateMcpAppBridgeOptions;
317
+ const replacementOnInitialized = vi.fn();
318
+ const params: McpAppInitializeParams = {
319
+ protocolVersion: "2026-01-26",
320
+ appInfo: { name: "example-app", version: "1.2.3" },
321
+ appCapabilities: { availableDisplayModes: ["inline", "pip"] },
322
+ };
323
+
324
+ rendered.rerender(view(replacementOnInitialized));
325
+ options.handlers?.onInitialized?.(params);
326
+
327
+ expect(onInitialized).not.toHaveBeenCalled();
328
+ expect(replacementOnInitialized).toHaveBeenCalledOnce();
329
+ expect(replacementOnInitialized).toHaveBeenCalledWith(params);
330
+ sandboxBridge.dispose();
331
+ });
332
+
275
333
  it("disposes the underlying bridge with the sandbox bridge", () => {
276
334
  let createBridge: SandboxHostProps["createBridge"] | null = null;
277
335
  sandboxHostMock.mockImplementation((props: SandboxHostProps) => {
@@ -350,7 +408,7 @@ describe("McpAppFrame", () => {
350
408
  );
351
409
  const options = createMcpAppBridgeMock.mock
352
410
  .calls[0]![0] as CreateMcpAppBridgeOptions;
353
- options.handlers?.onInitialized?.();
411
+ options.handlers?.onInitialized?.({});
354
412
 
355
413
  rendered.rerender(
356
414
  view({ displayMode: "inline", availableDisplayModes: ["inline", "pip"] }),
@@ -429,7 +487,7 @@ describe("McpAppFrame", () => {
429
487
 
430
488
  const options = createMcpAppBridgeMock.mock
431
489
  .calls[0]![0] as CreateMcpAppBridgeOptions;
432
- options.handlers?.onInitialized?.();
490
+ options.handlers?.onInitialized?.({});
433
491
 
434
492
  expect(bridge.notifyHostContextChanged).not.toHaveBeenCalled();
435
493
 
@@ -474,7 +532,7 @@ describe("McpAppFrame", () => {
474
532
  );
475
533
  const options = createMcpAppBridgeMock.mock
476
534
  .calls[0]![0] as CreateMcpAppBridgeOptions;
477
- options.handlers?.onInitialized?.();
535
+ options.handlers?.onInitialized?.({});
478
536
 
479
537
  rendered.rerender(view());
480
538
  expect(bridge.notifyHostContextChanged).not.toHaveBeenCalled();
@@ -519,7 +577,7 @@ describe("McpAppFrame", () => {
519
577
  );
520
578
  const options = createMcpAppBridgeMock.mock
521
579
  .calls[0]![0] as CreateMcpAppBridgeOptions;
522
- options.handlers?.onInitialized?.();
580
+ options.handlers?.onInitialized?.({});
523
581
 
524
582
  rendered.rerender(view(new Date(2)));
525
583
  expect(bridge.notifyHostContextChanged).toHaveBeenCalledTimes(1);
@@ -565,7 +623,7 @@ describe("McpAppFrame", () => {
565
623
  );
566
624
  const options = createMcpAppBridgeMock.mock
567
625
  .calls[0]![0] as CreateMcpAppBridgeOptions;
568
- options.handlers?.onInitialized?.();
626
+ options.handlers?.onInitialized?.({});
569
627
 
570
628
  rendered.rerender(view({}));
571
629
  expect(bridge.notifyHostContextChanged).not.toHaveBeenCalled();
@@ -143,9 +143,9 @@ function buildLiveHandlers(
143
143
  lifecycle.onSizeChange(p);
144
144
  live()?.onSizeChange?.(p);
145
145
  };
146
- out.onInitialized = () => {
146
+ out.onInitialized = (params) => {
147
147
  lifecycle.onInitialized();
148
- live()?.onInitialized?.();
148
+ live()?.onInitialized?.(params);
149
149
  };
150
150
  out.onRequestTeardown = (p) => live()?.onRequestTeardown?.(p);
151
151
  out.onLog = (p) => live()?.onLog?.(p);