@assistant-ui/react 0.15.17 → 0.15.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/context/providers/ThreadViewportProvider.js +2 -2
- package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
- package/dist/hooks/useToolCallElapsed.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -3
- package/dist/mcp-apps/McpAppRenderer.d.ts +30 -8
- package/dist/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/mcp-apps/McpAppRenderer.js +91 -47
- package/dist/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/mcp-apps/app-frame.d.ts.map +1 -1
- package/dist/mcp-apps/app-frame.js +57 -33
- package/dist/mcp-apps/app-frame.js.map +1 -1
- package/dist/mcp-apps/bridge.js +9 -4
- package/dist/mcp-apps/bridge.js.map +1 -1
- package/dist/mcp-apps/index.d.ts +2 -2
- package/dist/primitives/assistantModal/AssistantModalRoot.d.ts.map +1 -1
- package/dist/primitives/assistantModal/AssistantModalRoot.js +47 -79
- package/dist/primitives/assistantModal/AssistantModalRoot.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +61 -138
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/ComposerInput.js +3 -8
- package/dist/primitives/composer/ComposerInput.js.map +1 -1
- package/dist/primitives/composer/ComposerInputPluginContext.d.ts +1 -2
- package/dist/primitives/composer/ComposerInputPluginContext.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerInputPluginContext.js +1 -6
- package/dist/primitives/composer/ComposerInputPluginContext.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js +46 -15
- package/dist/primitives/composer/trigger/TriggerPopoverAction.js.map +1 -1
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js +40 -10
- package/dist/primitives/composer/trigger/TriggerPopoverDirective.js.map +1 -1
- package/dist/primitives/composer/trigger/matchesTriggerItemQuery.d.ts +6 -0
- package/dist/primitives/composer/trigger/matchesTriggerItemQuery.d.ts.map +1 -0
- package/dist/primitives/composer/trigger/matchesTriggerItemQuery.js +9 -0
- package/dist/primitives/composer/trigger/matchesTriggerItemQuery.js.map +1 -0
- package/dist/primitives/composer/trigger/triggerKeyboardResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerKeyboardResource.js +53 -61
- package/dist/primitives/composer/trigger/triggerKeyboardResource.js.map +1 -1
- package/dist/primitives/composer/trigger/triggerNavigationResource.d.ts.map +1 -1
- package/dist/primitives/composer/trigger/triggerNavigationResource.js +129 -141
- package/dist/primitives/composer/trigger/triggerNavigationResource.js.map +1 -1
- package/dist/primitives/message/MessageParts.js +2 -14
- package/dist/primitives/message/MessageParts.js.map +1 -1
- package/dist/primitives/message/MessagePartsGrouped.d.ts +3 -3
- package/dist/primitives/message/MessagePartsGrouped.js +2 -2
- package/dist/primitives/message/MessagePartsGrouped.js.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.d.ts +2 -0
- package/dist/primitives/messagePart/MessagePartText.d.ts.map +1 -1
- package/dist/primitives/messagePart/MessagePartText.js +13 -36
- package/dist/primitives/messagePart/MessagePartText.js.map +1 -1
- package/dist/primitives/reasoning/useScrollLock.js +3 -1
- package/dist/primitives/reasoning/useScrollLock.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +3 -3
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/sandbox-host/SandboxHost.d.ts.map +1 -1
- package/dist/sandbox-host/SandboxHost.js +36 -9
- package/dist/sandbox-host/SandboxHost.js.map +1 -1
- package/dist/unstable/useLiveCompletionAdapter.js +24 -2
- package/dist/unstable/useLiveCompletionAdapter.js.map +1 -1
- package/dist/unstable/useMentionAdapter.d.ts +9 -3
- package/dist/unstable/useMentionAdapter.d.ts.map +1 -1
- package/dist/unstable/useMentionAdapter.js +50 -43
- package/dist/unstable/useMentionAdapter.js.map +1 -1
- package/dist/unstable/useMessageStallDetection.d.ts +4 -5
- package/dist/unstable/useMessageStallDetection.d.ts.map +1 -1
- package/dist/unstable/useMessageStallDetection.js +16 -15
- package/dist/unstable/useMessageStallDetection.js.map +1 -1
- package/dist/unstable/useModelContextSnapshot.d.ts +34 -0
- package/dist/unstable/useModelContextSnapshot.d.ts.map +1 -0
- package/dist/unstable/useModelContextSnapshot.js +61 -0
- package/dist/unstable/useModelContextSnapshot.js.map +1 -0
- package/dist/unstable/useSlashCommandAdapter.d.ts.map +1 -1
- package/dist/unstable/useSlashCommandAdapter.js +2 -8
- package/dist/unstable/useSlashCommandAdapter.js.map +1 -1
- package/dist/unstable/webmcp/WebMcpRegistrationResource.d.ts +14 -0
- package/dist/unstable/webmcp/WebMcpRegistrationResource.d.ts.map +1 -0
- package/dist/unstable/webmcp/WebMcpRegistrationResource.js +85 -0
- package/dist/unstable/webmcp/WebMcpRegistrationResource.js.map +1 -0
- package/dist/unstable/webmcp/convertTools.d.ts +21 -0
- package/dist/unstable/webmcp/convertTools.d.ts.map +1 -0
- package/dist/unstable/webmcp/convertTools.js +161 -0
- package/dist/unstable/webmcp/convertTools.js.map +1 -0
- package/dist/unstable/webmcp/useWebMcpProvider.d.ts +21 -0
- package/dist/unstable/webmcp/useWebMcpProvider.d.ts.map +1 -0
- package/dist/unstable/webmcp/useWebMcpProvider.js +106 -0
- package/dist/unstable/webmcp/useWebMcpProvider.js.map +1 -0
- package/dist/unstable/webmcp/webmcp-host.d.ts +39 -0
- package/dist/unstable/webmcp/webmcp-host.d.ts.map +1 -0
- package/dist/unstable/webmcp/webmcp-host.js +29 -0
- package/dist/unstable/webmcp/webmcp-host.js.map +1 -0
- package/dist/utils/Primitive.d.ts +10 -2
- package/dist/utils/Primitive.d.ts.map +1 -1
- package/dist/utils/Primitive.js +25 -6
- package/dist/utils/Primitive.js.map +1 -1
- package/dist/utils/getSelectionMessageId.d.ts.map +1 -1
- package/dist/utils/getSelectionMessageId.js +17 -3
- package/dist/utils/getSelectionMessageId.js.map +1 -1
- package/package.json +13 -13
- package/src/context/providers/ThreadViewportProvider.tsx +2 -2
- package/src/hooks/useToolCallElapsed.ts +3 -0
- package/src/index.ts +14 -1
- package/src/mcp-apps/McpAppRenderer.test.tsx +253 -5
- package/src/mcp-apps/McpAppRenderer.tsx +147 -52
- package/src/mcp-apps/app-frame.test.tsx +437 -1
- package/src/mcp-apps/app-frame.tsx +76 -21
- package/src/mcp-apps/bridge.test.ts +98 -0
- package/src/mcp-apps/bridge.ts +14 -3
- package/src/mcp-apps/index.ts +5 -1
- package/src/primitives/assistantModal/AssistantModalRoot.test.tsx +123 -0
- package/src/primitives/assistantModal/AssistantModalRoot.tsx +11 -31
- package/src/primitives/assistantModal/AssistantModalTrigger.test.tsx +1 -16
- package/src/primitives/composer/ComposerAttachmentDropzone.test.tsx +43 -0
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +2 -10
- package/src/primitives/composer/ComposerInput.test.tsx +22 -0
- package/src/primitives/composer/ComposerInput.tsx +5 -9
- package/src/primitives/composer/ComposerInputPluginContext.tsx +0 -10
- package/src/primitives/composer/trigger/TriggerPopoverAction.tsx +4 -2
- package/src/primitives/composer/trigger/TriggerPopoverBehavior.test.tsx +109 -0
- package/src/primitives/composer/trigger/TriggerPopoverDirective.tsx +4 -2
- package/src/primitives/composer/trigger/matchesTriggerItemQuery.test.ts +52 -0
- package/src/primitives/composer/trigger/matchesTriggerItemQuery.ts +13 -0
- package/src/primitives/composer/trigger/triggerKeyboardResource.ts +13 -7
- package/src/primitives/composer/trigger/triggerNavigationResource.ts +8 -13
- package/src/primitives/message/MessageParts.tsx +1 -21
- package/src/primitives/message/MessagePartsGrouped.test.tsx +93 -0
- package/src/primitives/message/MessagePartsGrouped.tsx +6 -6
- package/src/primitives/messagePart/MessagePartText.test.tsx +69 -0
- package/src/primitives/messagePart/MessagePartText.tsx +22 -9
- package/src/primitives/reasoning/useScrollLock.test.tsx +129 -0
- package/src/primitives/reasoning/useScrollLock.ts +15 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.test.tsx +38 -1
- package/src/primitives/thread/useThreadViewportAutoScroll.ts +7 -6
- package/src/sandbox-host/SandboxHost.test.tsx +160 -2
- package/src/sandbox-host/SandboxHost.tsx +50 -10
- package/src/tests/MessageParts.rendererOptions.test.tsx +117 -0
- package/src/tests/toolCallTiming.test.tsx +55 -1
- package/src/unstable/useComposerInputHistory.test.tsx +2 -1
- package/src/unstable/useLiveCompletionAdapter.test.tsx +202 -1
- package/src/unstable/useLiveCompletionAdapter.ts +38 -8
- package/src/unstable/useMentionAdapter.test.tsx +383 -0
- package/src/unstable/useMentionAdapter.ts +94 -57
- package/src/unstable/useMessageStallDetection.ts +26 -19
- package/src/unstable/useModelContextSnapshot.test.tsx +132 -0
- package/src/unstable/useModelContextSnapshot.ts +67 -0
- package/src/unstable/useSlashCommandAdapter.ts +2 -9
- package/src/unstable/webmcp/WebMcpRegistrationResource.ts +92 -0
- package/src/unstable/webmcp/__tests__/webmcp.fake.tsx +168 -0
- package/src/unstable/webmcp/convertTools.test.ts +553 -0
- package/src/unstable/webmcp/convertTools.ts +260 -0
- package/src/unstable/webmcp/useWebMcpProvider.test.tsx +466 -0
- package/src/unstable/webmcp/useWebMcpProvider.ts +148 -0
- package/src/unstable/webmcp/webmcp-host.test.ts +142 -0
- package/src/unstable/webmcp/webmcp-host.ts +83 -0
- package/src/utils/Primitive.test.tsx +46 -1
- package/src/utils/Primitive.tsx +37 -7
- package/src/utils/getSelectionMessageId.test.ts +176 -1
- package/src/utils/getSelectionMessageId.ts +21 -3
- package/dist/utils/invokeCallbackSafely.d.ts +0 -5
- package/dist/utils/invokeCallbackSafely.d.ts.map +0 -1
- package/dist/utils/invokeCallbackSafely.js +0 -15
- package/dist/utils/invokeCallbackSafely.js.map +0 -1
- package/dist/utils/useToolArgsFieldStatus.d.ts +0 -19
- package/dist/utils/useToolArgsFieldStatus.d.ts.map +0 -1
- package/dist/utils/useToolArgsFieldStatus.js +0 -23
- package/dist/utils/useToolArgsFieldStatus.js.map +0 -1
- package/src/utils/invokeCallbackSafely.ts +0 -11
- package/src/utils/useToolArgsFieldStatus.ts +0 -18
|
@@ -6,10 +6,13 @@ import {
|
|
|
6
6
|
useMemo,
|
|
7
7
|
useRef,
|
|
8
8
|
useState,
|
|
9
|
-
type MutableRefObject,
|
|
10
9
|
type ReactNode,
|
|
11
10
|
} from "react";
|
|
12
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
McpAppMetadata,
|
|
13
|
+
TextMessagePart,
|
|
14
|
+
ToolCallMessagePart,
|
|
15
|
+
} from "@assistant-ui/core";
|
|
13
16
|
import type {
|
|
14
17
|
ToolCallMessagePartComponent,
|
|
15
18
|
ToolCallMessagePartProps,
|
|
@@ -30,13 +33,11 @@ import type {
|
|
|
30
33
|
import { getMcpAppFromToolPart } from "./utils";
|
|
31
34
|
import { isRecord } from "@assistant-ui/core/internal";
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*/
|
|
39
|
-
host: ResourceElement<McpAppsHost>;
|
|
36
|
+
/**
|
|
37
|
+
* Options that apply to a single MCP app. `McpAppRenderer` takes them as
|
|
38
|
+
* thread-wide defaults, and its `forPart` resolver overrides them for one part.
|
|
39
|
+
*/
|
|
40
|
+
export type McpAppPartOptions = {
|
|
40
41
|
/** Sandbox + container styling. Passes through to SafeContentFrame. */
|
|
41
42
|
sandbox?: McpAppSandboxConfig;
|
|
42
43
|
/**
|
|
@@ -51,6 +52,10 @@ export type McpAppRendererOptions = {
|
|
|
51
52
|
/**
|
|
52
53
|
* Optional widget interaction and lifecycle handlers. Data-plane handlers
|
|
53
54
|
* (`callTool`, `readResource`, `listResources`) always use `host`.
|
|
55
|
+
*
|
|
56
|
+
* Which handlers a widget may call is captured when its frame mounts, so a
|
|
57
|
+
* `forPart` resolver returns the same handler keys for a part on every
|
|
58
|
+
* render even when the implementations change.
|
|
54
59
|
*/
|
|
55
60
|
handlers?: Omit<
|
|
56
61
|
McpAppBridgeHandlers,
|
|
@@ -64,6 +69,26 @@ export type McpAppRendererOptions = {
|
|
|
64
69
|
errorFallback?: ReactNode | ((error: Error) => ReactNode);
|
|
65
70
|
};
|
|
66
71
|
|
|
72
|
+
export type McpAppRendererOptions = McpAppPartOptions & {
|
|
73
|
+
/**
|
|
74
|
+
* Provides the data-plane operations the widget can request
|
|
75
|
+
* (`loadResource`, `callTool`, `readResource`, `listResources`). Use
|
|
76
|
+
* `McpAppsRemoteHost({ url })` for the default HTTP-route convention.
|
|
77
|
+
*/
|
|
78
|
+
host: ResourceElement<McpAppsHost>;
|
|
79
|
+
/**
|
|
80
|
+
* Resolves the options for one part. Each returned value replaces the
|
|
81
|
+
* thread-wide option of the same name, so a host can give every MCP app its
|
|
82
|
+
* own display mode and answer `requestDisplayMode` for the part that asked.
|
|
83
|
+
*
|
|
84
|
+
* `handlers` is the exception and merges per key. Its keys are capabilities
|
|
85
|
+
* negotiated with the widget rather than data, so replacing the whole bag to
|
|
86
|
+
* add one handler would withdraw the others from that part's widget with
|
|
87
|
+
* nothing to observe it by.
|
|
88
|
+
*/
|
|
89
|
+
forPart?: (part: ToolCallMessagePart) => McpAppPartOptions;
|
|
90
|
+
};
|
|
91
|
+
|
|
67
92
|
type LoadedResourceState = {
|
|
68
93
|
host: McpAppsHost;
|
|
69
94
|
resourceUri: string;
|
|
@@ -72,7 +97,31 @@ type LoadedResourceState = {
|
|
|
72
97
|
error?: Error;
|
|
73
98
|
};
|
|
74
99
|
|
|
75
|
-
type
|
|
100
|
+
type RendererState = {
|
|
101
|
+
host: McpAppsHost;
|
|
102
|
+
options: McpAppRendererOptions;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
type UseRendererStore = UseBoundStore<StoreApi<RendererState>>;
|
|
106
|
+
|
|
107
|
+
// Callers pass options as an object literal, so the resource hands this a fresh
|
|
108
|
+
// identity on every run; comparing the values keeps an unchanged run from
|
|
109
|
+
// re-rendering every mounted part. `host` is excluded because it is an element
|
|
110
|
+
// rebuilt on every run, and the store carries the resolved host separately.
|
|
111
|
+
const isSameOptions = (
|
|
112
|
+
a: McpAppRendererOptions,
|
|
113
|
+
b: McpAppRendererOptions,
|
|
114
|
+
): boolean => {
|
|
115
|
+
const aKeys = Object.keys(a).filter((key) => key !== "host");
|
|
116
|
+
const bKeys = Object.keys(b).filter((key) => key !== "host");
|
|
117
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
118
|
+
const aValues = a as Record<string, unknown>;
|
|
119
|
+
const bValues = b as Record<string, unknown>;
|
|
120
|
+
return aKeys.every(
|
|
121
|
+
(key) =>
|
|
122
|
+
Object.hasOwn(bValues, key) && Object.is(aValues[key], bValues[key]),
|
|
123
|
+
);
|
|
124
|
+
};
|
|
76
125
|
|
|
77
126
|
function getInput(part: {
|
|
78
127
|
status: { type: string };
|
|
@@ -92,27 +141,67 @@ const defaultOpenLink = ({ url }: { url: string }) => {
|
|
|
92
141
|
window.open(url, "_blank", "noopener,noreferrer");
|
|
93
142
|
};
|
|
94
143
|
|
|
95
|
-
function
|
|
96
|
-
if (typeof params === "string") return params;
|
|
144
|
+
function extractSendMessageTexts(params: unknown): string[] | undefined {
|
|
145
|
+
if (typeof params === "string") return [params];
|
|
97
146
|
if (!params || typeof params !== "object") return undefined;
|
|
98
147
|
const obj = params as Record<string, unknown>;
|
|
99
|
-
if (typeof obj["prompt"] === "string") return obj["prompt"];
|
|
100
|
-
if (typeof obj["text"] === "string") return obj["text"];
|
|
101
|
-
if (typeof obj["message"] === "string") return obj["message"];
|
|
148
|
+
if (typeof obj["prompt"] === "string") return [obj["prompt"]];
|
|
149
|
+
if (typeof obj["text"] === "string") return [obj["text"]];
|
|
150
|
+
if (typeof obj["message"] === "string") return [obj["message"]];
|
|
151
|
+
if (Array.isArray(obj["content"])) {
|
|
152
|
+
return obj["content"]
|
|
153
|
+
.filter(
|
|
154
|
+
(block): block is { type: "text"; text: string } =>
|
|
155
|
+
isRecord(block) &&
|
|
156
|
+
block["type"] === "text" &&
|
|
157
|
+
typeof block["text"] === "string",
|
|
158
|
+
)
|
|
159
|
+
.map((block) => block.text);
|
|
160
|
+
}
|
|
102
161
|
return undefined;
|
|
103
162
|
}
|
|
104
163
|
|
|
164
|
+
function extractSendMessageContent(
|
|
165
|
+
params: unknown,
|
|
166
|
+
): { content: TextMessagePart[] } | { reason: string } {
|
|
167
|
+
const texts = extractSendMessageTexts(params);
|
|
168
|
+
if (!texts) return { reason: "unrecognised params shape" };
|
|
169
|
+
const content = texts
|
|
170
|
+
.filter((text) => text !== "")
|
|
171
|
+
.map((text): TextMessagePart => ({ type: "text", text }));
|
|
172
|
+
if (content.length === 0) {
|
|
173
|
+
return { reason: "no text content to append" };
|
|
174
|
+
}
|
|
175
|
+
return { content };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function resolvePartOptions(
|
|
179
|
+
options: McpAppRendererOptions,
|
|
180
|
+
part: ToolCallMessagePartProps,
|
|
181
|
+
): McpAppRendererOptions {
|
|
182
|
+
const overrides = options.forPart?.(part);
|
|
183
|
+
if (!overrides) return options;
|
|
184
|
+
const handlers =
|
|
185
|
+
options.handlers && overrides.handlers
|
|
186
|
+
? { ...options.handlers, ...overrides.handlers }
|
|
187
|
+
: (overrides.handlers ?? options.handlers);
|
|
188
|
+
return {
|
|
189
|
+
...options,
|
|
190
|
+
...overrides,
|
|
191
|
+
...(handlers === undefined ? {} : { handlers }),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
105
195
|
function InlineRenderer({
|
|
106
196
|
part,
|
|
107
|
-
|
|
108
|
-
optionsRef,
|
|
197
|
+
useRendererStore,
|
|
109
198
|
}: {
|
|
110
199
|
part: ToolCallMessagePartProps;
|
|
111
|
-
|
|
112
|
-
optionsRef: MutableRefObject<McpAppRendererOptions>;
|
|
200
|
+
useRendererStore: UseRendererStore;
|
|
113
201
|
}) {
|
|
114
|
-
const opts = optionsRef.current;
|
|
115
202
|
const aui = useAui();
|
|
203
|
+
const rendererOptions = useRendererStore((state) => state.options);
|
|
204
|
+
const opts = resolvePartOptions(rendererOptions, part);
|
|
116
205
|
const app = getMcpAppFromToolPart(part);
|
|
117
206
|
const cachedAppRef = useRef<McpAppMetadata | undefined>(undefined);
|
|
118
207
|
useLayoutEffect(() => {
|
|
@@ -122,7 +211,7 @@ function InlineRenderer({
|
|
|
122
211
|
|
|
123
212
|
const [loadedResource, setLoadedResource] = useState<LoadedResourceState>();
|
|
124
213
|
|
|
125
|
-
const host =
|
|
214
|
+
const host = useRendererStore((state) => state.host);
|
|
126
215
|
const resourceUri = appForRender?.resourceUri;
|
|
127
216
|
const serverId = appForRender?.serverId;
|
|
128
217
|
const callerHandlers = opts.handlers;
|
|
@@ -133,12 +222,16 @@ function InlineRenderer({
|
|
|
133
222
|
const targetUri = resourceUri;
|
|
134
223
|
const targetServerId = serverId;
|
|
135
224
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
225
|
+
// Host changes are published later in this passive flush. Defer until that
|
|
226
|
+
// publication lands, then verify this effect still owns the current host.
|
|
227
|
+
const loadResource = async () => {
|
|
228
|
+
await Promise.resolve();
|
|
229
|
+
if (cancelled || useRendererStore.getState().host !== targetHost) return;
|
|
230
|
+
try {
|
|
231
|
+
const res = await targetHost.loadResource({
|
|
232
|
+
uri: targetUri,
|
|
233
|
+
...(targetServerId ? { serverId: targetServerId } : {}),
|
|
234
|
+
});
|
|
142
235
|
if (!cancelled)
|
|
143
236
|
setLoadedResource({
|
|
144
237
|
host: targetHost,
|
|
@@ -148,8 +241,7 @@ function InlineRenderer({
|
|
|
148
241
|
: {}),
|
|
149
242
|
resource: res,
|
|
150
243
|
});
|
|
151
|
-
})
|
|
152
|
-
.catch((error: unknown) => {
|
|
244
|
+
} catch (error: unknown) {
|
|
153
245
|
if (!cancelled) {
|
|
154
246
|
setLoadedResource({
|
|
155
247
|
host: targetHost,
|
|
@@ -160,12 +252,14 @@ function InlineRenderer({
|
|
|
160
252
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
161
253
|
});
|
|
162
254
|
}
|
|
163
|
-
}
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
void loadResource();
|
|
164
258
|
|
|
165
259
|
return () => {
|
|
166
260
|
cancelled = true;
|
|
167
261
|
};
|
|
168
|
-
}, [host, resourceUri, serverId]);
|
|
262
|
+
}, [host, resourceUri, serverId, useRendererStore]);
|
|
169
263
|
|
|
170
264
|
const bridgeHandlers = useMemo<McpAppBridgeHandlers>(
|
|
171
265
|
() => ({
|
|
@@ -174,32 +268,34 @@ function InlineRenderer({
|
|
|
174
268
|
sendMessage:
|
|
175
269
|
callerHandlers?.sendMessage ??
|
|
176
270
|
((params) => {
|
|
177
|
-
const
|
|
178
|
-
if (
|
|
179
|
-
|
|
271
|
+
const result = extractSendMessageContent(params);
|
|
272
|
+
if ("reason" in result) {
|
|
273
|
+
return { isError: true, ok: false, reason: result.reason };
|
|
274
|
+
}
|
|
275
|
+
aui.thread.append({ content: result.content });
|
|
180
276
|
return { ok: true };
|
|
181
277
|
}),
|
|
182
278
|
callTool: (params) =>
|
|
183
|
-
|
|
279
|
+
useRendererStore.getState().host.callTool({
|
|
184
280
|
...params,
|
|
185
281
|
...(serverId ? { serverId } : {}),
|
|
186
282
|
}),
|
|
187
283
|
readResource: (params) =>
|
|
188
|
-
|
|
284
|
+
useRendererStore.getState().host.readResource({
|
|
189
285
|
...params,
|
|
190
286
|
...(serverId ? { serverId } : {}),
|
|
191
287
|
}),
|
|
192
288
|
listResources: (params) => {
|
|
193
289
|
if (!serverId) {
|
|
194
|
-
return
|
|
290
|
+
return useRendererStore.getState().host.listResources(params);
|
|
195
291
|
}
|
|
196
|
-
return
|
|
292
|
+
return useRendererStore.getState().host.listResources({
|
|
197
293
|
...(isRecord(params) ? params : {}),
|
|
198
294
|
serverId,
|
|
199
295
|
});
|
|
200
296
|
},
|
|
201
297
|
}),
|
|
202
|
-
[aui, callerHandlers, serverId,
|
|
298
|
+
[aui, callerHandlers, serverId, useRendererStore],
|
|
203
299
|
);
|
|
204
300
|
|
|
205
301
|
const loadedResourceForApp =
|
|
@@ -253,27 +349,26 @@ const useMcpAppRenderer = (
|
|
|
253
349
|
): { readonly render: ToolCallMessagePartComponent } => {
|
|
254
350
|
const host = useResource(options.host);
|
|
255
351
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
create<{ host: McpAppsHost }>(() => ({ host })),
|
|
352
|
+
// The rendered component identity has to stay stable, or every part remounts
|
|
353
|
+
// its frame; options reach the parts through the store instead of props.
|
|
354
|
+
const [useRendererStore] = useState(() =>
|
|
355
|
+
create<RendererState>(() => ({ host, options })),
|
|
261
356
|
);
|
|
262
357
|
useEffect(() => {
|
|
263
|
-
|
|
264
|
-
|
|
358
|
+
useRendererStore.setState((prev) =>
|
|
359
|
+
prev.host === host && isSameOptions(prev.options, options)
|
|
360
|
+
? prev
|
|
361
|
+
: { host, options },
|
|
362
|
+
);
|
|
363
|
+
}, [host, options, useRendererStore]);
|
|
265
364
|
|
|
266
365
|
const render = useMemo((): ToolCallMessagePartComponent => {
|
|
267
366
|
const Render: ToolCallMessagePartComponent = (props) => (
|
|
268
|
-
<InlineRenderer
|
|
269
|
-
part={props}
|
|
270
|
-
useHostStore={useHostStore}
|
|
271
|
-
optionsRef={optionsRef}
|
|
272
|
-
/>
|
|
367
|
+
<InlineRenderer part={props} useRendererStore={useRendererStore} />
|
|
273
368
|
);
|
|
274
369
|
Render.displayName = "McpAppRenderer";
|
|
275
370
|
return Render;
|
|
276
|
-
}, [
|
|
371
|
+
}, [useRendererStore]);
|
|
277
372
|
|
|
278
373
|
return { render };
|
|
279
374
|
};
|