@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.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/mcp-apps/app-frame.js +2 -2
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/mcp-apps/bridge.d.ts.map +1 -1
- package/dist/mcp-apps/bridge.js +28 -3
- package/dist/mcp-apps/bridge.js.map +1 -1
- package/dist/mcp-apps/index.d.ts +1 -1
- package/dist/mcp-apps/index.d.ts.map +1 -1
- package/dist/mcp-apps/types.d.ts +18 -1
- package/dist/mcp-apps/types.d.ts.map +1 -1
- package/dist/mcp-apps/types.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +14 -12
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerRoot.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerRoot.js +63 -55
- package/dist/primitives/composer/ComposerRoot.js.map +1 -1
- package/dist/unstable/useComposerInput.d.ts +2 -0
- package/dist/unstable/useComposerInput.d.ts.map +1 -1
- package/dist/unstable/useComposerInput.js +2 -0
- package/dist/unstable/useComposerInput.js.map +1 -1
- package/package.json +7 -7
- package/src/index.ts +1 -0
- package/src/mcp-apps/McpAppRenderer.test.tsx +7 -2
- package/src/mcp-apps/app-frame.test.tsx +64 -6
- package/src/mcp-apps/app-frame.tsx +2 -2
- package/src/mcp-apps/bridge.test.ts +154 -0
- package/src/mcp-apps/bridge.ts +51 -9
- package/src/mcp-apps/index.ts +1 -0
- package/src/mcp-apps/types.ts +15 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +32 -1
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +14 -12
- package/src/primitives/composer/ComposerDisabledThread.test.tsx +121 -0
- package/src/primitives/composer/ComposerRoot.test.tsx +21 -1
- package/src/primitives/composer/ComposerRoot.tsx +8 -1
- 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","
|
|
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,
|
|
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(
|
|
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] =
|
|
59
|
-
$[3] =
|
|
60
|
-
$[4] =
|
|
60
|
+
$[2] = inert;
|
|
61
|
+
$[3] = onMouseDown;
|
|
62
|
+
$[4] = onSubmit;
|
|
63
|
+
$[5] = rest;
|
|
61
64
|
} else {
|
|
62
65
|
compact = $[1];
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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 ($[
|
|
75
|
+
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
|
72
76
|
t1 = { setMultiline };
|
|
73
|
-
$[
|
|
74
|
-
} else t1 = $[
|
|
77
|
+
$[6] = t1;
|
|
78
|
+
} else t1 = $[6];
|
|
75
79
|
const compactContext = t1;
|
|
76
80
|
let t2;
|
|
77
|
-
if ($[
|
|
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
|
-
$[
|
|
80
|
-
$[
|
|
81
|
-
} else t2 = $[
|
|
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 ($[
|
|
89
|
+
if ($[9] !== send) {
|
|
85
90
|
t3 = (e) => {
|
|
86
91
|
e.preventDefault();
|
|
87
92
|
if (!send) return;
|
|
88
93
|
send();
|
|
89
94
|
};
|
|
90
|
-
$[
|
|
91
|
-
$[
|
|
92
|
-
} else t3 = $[
|
|
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 =
|
|
97
|
-
|
|
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 ($[
|
|
106
|
-
t7 = composeEventHandlers(
|
|
107
|
-
$[
|
|
108
|
-
$[
|
|
109
|
-
|
|
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 ($[
|
|
112
|
-
t8 =
|
|
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":
|
|
122
|
+
"data-compact": t6,
|
|
116
123
|
ref: forwardedRef,
|
|
117
|
-
onSubmit:
|
|
118
|
-
onMouseDown:
|
|
124
|
+
onSubmit: t7,
|
|
125
|
+
onMouseDown: t8
|
|
119
126
|
});
|
|
120
|
-
$[
|
|
121
|
-
$[
|
|
122
|
-
$[
|
|
123
|
-
$[
|
|
124
|
-
$[
|
|
125
|
-
$[
|
|
126
|
-
$[
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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:
|
|
140
|
+
children: t9
|
|
133
141
|
});
|
|
134
|
-
$[
|
|
135
|
-
$[
|
|
136
|
-
$[
|
|
137
|
-
} else
|
|
138
|
-
return
|
|
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","
|
|
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
|
|
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":"
|
|
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.
|
|
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.
|
|
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.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.
|
|
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
|
@@ -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
|
-
|
|
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 {
|
|
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);
|