@epam/ai-dial-mcp-apps 1.1.0-dev.405
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/components/McpAppInlinePreview/McpAppInlinePreview.d.ts +25 -0
- package/components/McpAppInlinePreview/McpAppInlinePreview.d.ts.map +1 -0
- package/hooks/useMcpAppInlinePreview/useMcpAppInlinePreview.d.ts +28 -0
- package/hooks/useMcpAppInlinePreview/useMcpAppInlinePreview.d.ts.map +1 -0
- package/hooks/useMcpAppResponseCache/useMcpAppResponseCache.d.ts +26 -0
- package/hooks/useMcpAppResponseCache/useMcpAppResponseCache.d.ts.map +1 -0
- package/index.d.ts +7 -0
- package/index.d.ts.map +1 -0
- package/index.js +216 -0
- package/models/mcp-apps.d.ts +102 -0
- package/models/mcp-apps.d.ts.map +1 -0
- package/package.json +32 -0
- package/utils/mcp-app.d.ts +93 -0
- package/utils/mcp-app.d.ts.map +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { McpAppHostAdapter, McpAppResponseCache, McpAppToolCallSeed, McpAppToolRef } from '../../models/mcp-apps';
|
|
2
|
+
/** Props for the `McpAppInlinePreview` component. */
|
|
3
|
+
export interface McpAppInlinePreviewProps {
|
|
4
|
+
/** Matched MCP App tool to preview. */
|
|
5
|
+
match: McpAppToolRef;
|
|
6
|
+
/** Original tool call's arguments/result, seeding the mounted app's initial state. */
|
|
7
|
+
toolCall?: McpAppToolCallSeed;
|
|
8
|
+
/** Shared cache (with a full-width canvas) so switching to it for this message reuses this preview's fetch instead of repeating it. */
|
|
9
|
+
cache: McpAppResponseCache;
|
|
10
|
+
/** This message's cache key (e.g. `mcpAppCanvasKey(index)`). */
|
|
11
|
+
cacheKey: string;
|
|
12
|
+
/** Host-supplied dependencies — display context, sandbox URL, and the resource-fetch/tool-call functions. */
|
|
13
|
+
hostAdapter: McpAppHostAdapter;
|
|
14
|
+
/** Called when the user activates the expand-to-canvas button. */
|
|
15
|
+
onExpand: () => void;
|
|
16
|
+
/** Accessible label for the expand-to-canvas button. */
|
|
17
|
+
expandAriaLabel: string;
|
|
18
|
+
/** Accessible label for the reload button. */
|
|
19
|
+
reloadAriaLabel: string;
|
|
20
|
+
/** Message shown when the resource fails to load or the app fails to initialize. */
|
|
21
|
+
loadErrorLabel: string;
|
|
22
|
+
}
|
|
23
|
+
/** Renders a compact, always-visible preview of a message's matched MCP App — see `McpAppInlinePreviewProps`. */
|
|
24
|
+
export declare const McpAppInlinePreview: import('react').NamedExoticComponent<McpAppInlinePreviewProps>;
|
|
25
|
+
//# sourceMappingURL=McpAppInlinePreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpAppInlinePreview.d.ts","sourceRoot":"","sources":["../../../src/components/McpAppInlinePreview/McpAppInlinePreview.tsx"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAC;AAE/B,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,KAAK,EAAE,aAAa,CAAC;IACrB,sFAAsF;IACtF,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,uIAAuI;IACvI,KAAK,EAAE,mBAAmB,CAAC;IAC3B,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,6GAA6G;IAC7G,WAAW,EAAE,iBAAiB,CAAC;IAC/B,kEAAkE;IAClE,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,wDAAwD;IACxD,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,cAAc,EAAE,MAAM,CAAC;CACxB;AAmFD,iHAAiH;AACjH,eAAO,MAAM,mBAAmB,gEAAgC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { McpAppCanvasContent } from '@epam/ai-dial-attachment-canvas';
|
|
2
|
+
import { McpAppInlinePreviewStatus, McpAppHostAdapter, McpAppResponseCache, McpAppToolCallSeed, McpAppToolRef } from '../../models/mcp-apps';
|
|
3
|
+
/** State returned by `useMcpAppInlinePreview`. */
|
|
4
|
+
export interface McpAppInlinePreviewState {
|
|
5
|
+
/** Current load state of the preview's fetch. */
|
|
6
|
+
status: McpAppInlinePreviewStatus;
|
|
7
|
+
/** Present only when `status` is `Ready`. */
|
|
8
|
+
content?: McpAppCanvasContent;
|
|
9
|
+
/** Re-fetches the resource and re-resolves the tool result from scratch, bypassing `cache`. */
|
|
10
|
+
reload: () => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Fetches an MCP App's `ui://` resource and resolves its seeded tool result
|
|
14
|
+
* for a compact, inline preview embedded directly under a message — a
|
|
15
|
+
* lighter-weight sibling of a full-width canvas that mounts independently of
|
|
16
|
+
* any canvas/side-panel machinery, so it can render alongside the message
|
|
17
|
+
* body instead of taking over the canvas. Builds `content.hostContext` from
|
|
18
|
+
* `hostAdapter.hostContext` so the app can render a layout appropriate to
|
|
19
|
+
* where it's mounted.
|
|
20
|
+
*
|
|
21
|
+
* `cache` (shared with a full-width canvas via the same
|
|
22
|
+
* `useMcpAppResponseCache` instance, keyed by `cacheKey`) is checked before
|
|
23
|
+
* fetching — switching from this preview to the full canvas (or back) for
|
|
24
|
+
* the same message reuses the same fetch/live-tool-re-call instead of
|
|
25
|
+
* repeating it. Call the returned `reload` to bypass and refresh it.
|
|
26
|
+
*/
|
|
27
|
+
export declare const useMcpAppInlinePreview: (match: McpAppToolRef | undefined, toolCall: McpAppToolCallSeed | undefined, cache: McpAppResponseCache, cacheKey: string, hostAdapter: McpAppHostAdapter) => McpAppInlinePreviewState;
|
|
28
|
+
//# sourceMappingURL=useMcpAppInlinePreview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMcpAppInlinePreview.d.ts","sourceRoot":"","sources":["../../../src/hooks/useMcpAppInlinePreview/useMcpAppInlinePreview.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AAGzC,OAAO,EACL,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,uBAAuB,CAAC;AAM/B,kDAAkD;AAClD,MAAM,WAAW,wBAAwB;IACvC,iDAAiD;IACjD,MAAM,EAAE,yBAAyB,CAAC;IAClC,6CAA6C;IAC7C,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,+FAA+F;IAC/F,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,GACjC,OAAO,aAAa,GAAG,SAAS,EAChC,UAAU,kBAAkB,GAAG,SAAS,EACxC,OAAO,mBAAmB,EAC1B,UAAU,MAAM,EAChB,aAAa,iBAAiB,KAC7B,wBAgGF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { McpAppResponseCache } from '../../models/mcp-apps';
|
|
2
|
+
/**
|
|
3
|
+
* In-memory cache of a fetched MCP App resource + its resolved tool result,
|
|
4
|
+
* keyed by a caller-supplied `key` (e.g. `mcpAppCanvasKey(messageIndex)`),
|
|
5
|
+
* scoped to one open conversation. Lets an inline preview and a full-width
|
|
6
|
+
* canvas reuse the same fetch/live-tool-re-call for the same message instead
|
|
7
|
+
* of repeating it every time the user switches between the two.
|
|
8
|
+
*
|
|
9
|
+
* Each entry is tagged with the `seedKey` (`computeMcpAppSeedKey`) it was
|
|
10
|
+
* resolved from and a 15-minute TTL: a freshly-streamed message mounts the
|
|
11
|
+
* preview before the tool call is known, so the first write happens with an
|
|
12
|
+
* undefined seed. `get` treats a later, settled seed as a miss rather than
|
|
13
|
+
* reusing that earlier, seedless entry.
|
|
14
|
+
*
|
|
15
|
+
* The underlying `Map`s (entries, and `getOrFetch`'s in-flight promises)
|
|
16
|
+
* live in `useRef`s, mutated only inside `get`/`set`/`invalidate`/`getOrFetch`
|
|
17
|
+
* — functions called later by consumers (in an effect or event handler),
|
|
18
|
+
* never during this hook's own render — so no ref is read or written while
|
|
19
|
+
* rendering. Entries are namespaced by `conversationId`
|
|
20
|
+
* (`${conversationId}:${key}`) rather than clearing the map on conversation
|
|
21
|
+
* switch, since clearing would itself require touching the ref during
|
|
22
|
+
* render; `set` opportunistically prunes expired entries instead, keeping
|
|
23
|
+
* the map bounded without a per-conversation reset.
|
|
24
|
+
*/
|
|
25
|
+
export declare const useMcpAppResponseCache: (conversationId: string) => McpAppResponseCache;
|
|
26
|
+
//# sourceMappingURL=useMcpAppResponseCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useMcpAppResponseCache.d.ts","sourceRoot":"","sources":["../../../src/hooks/useMcpAppResponseCache/useMcpAppResponseCache.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAW/B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,sBAAsB,GACjC,gBAAgB,MAAM,KACrB,mBAyEF,CAAC"}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { McpAppInlinePreview, type McpAppInlinePreviewProps, } from './components/McpAppInlinePreview/McpAppInlinePreview';
|
|
2
|
+
export { useMcpAppInlinePreview, type McpAppInlinePreviewState, } from './hooks/useMcpAppInlinePreview/useMcpAppInlinePreview';
|
|
3
|
+
export { useMcpAppResponseCache } from './hooks/useMcpAppResponseCache/useMcpAppResponseCache';
|
|
4
|
+
export type { McpDeploymentKind, McpAppToolDiscovery, McpAppToolRef, McpAppToolCallSeed, FetchMcpAppResourceHtml, CallMcpAppTool, CachedMcpAppResponse, McpAppResponseCache, McpAppHostAdapter, } from './models/mcp-apps';
|
|
5
|
+
export { McpAppInlinePreviewStatus } from './models/mcp-apps';
|
|
6
|
+
export { collectToolCallNames, findMcpAppForMessage, mcpAppCanvasKey, computeMcpAppSeedKey, resolveMcpAppToolCallSeed, resolveMcpAppToolResult, } from './utils/mcp-app';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,GAC9B,MAAM,sDAAsD,CAAC;AAC9D,OAAO,EACL,sBAAsB,EACtB,KAAK,wBAAwB,GAC9B,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uDAAuD,CAAC;AAC/F,YAAY,EACV,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC"}
|
package/index.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { AttachmentContentType as e, McpAppCanvasRenderer as t } from "@epam/ai-dial-attachment-canvas";
|
|
2
|
+
import { DIAL_KIT_ICON_STROKE as n, GhostIconButton as r, Spinner as i } from "@epam/ai-dial-ui-kit";
|
|
3
|
+
import { IconAlertTriangle as a, IconArrowsMaximize as o, IconRefresh as s } from "@tabler/icons-react";
|
|
4
|
+
import { memo as c, useCallback as l, useEffect as u, useMemo as d, useRef as f, useState as p } from "react";
|
|
5
|
+
import { MessageRole as m } from "@epam/ai-dial-chat-shared";
|
|
6
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
7
|
+
//#region src/models/mcp-apps.ts
|
|
8
|
+
var _ = /* @__PURE__ */ function(e) {
|
|
9
|
+
return e.Loading = "loading", e.Ready = "ready", e.Error = "error", e.Unavailable = "unavailable", e;
|
|
10
|
+
}({}), v = (e) => {
|
|
11
|
+
try {
|
|
12
|
+
let t = JSON.parse(e);
|
|
13
|
+
return typeof t == "object" && t ? t : {};
|
|
14
|
+
} catch {
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
}, y = (e) => {
|
|
18
|
+
let t = /* @__PURE__ */ new Map();
|
|
19
|
+
for (let n of e?.tool_messages ?? []) {
|
|
20
|
+
for (let e of n.tool_calls ?? []) t.set(e.id, {
|
|
21
|
+
name: e.name,
|
|
22
|
+
args: e.args
|
|
23
|
+
});
|
|
24
|
+
if (n.type === "tool" && n.tool_call_id) {
|
|
25
|
+
let e = t.get(n.tool_call_id);
|
|
26
|
+
e && (e.result = n.content);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (let n of e?.tool_execution_history ?? []) {
|
|
30
|
+
for (let e of n.tool_calls ?? []) t.set(e.id, {
|
|
31
|
+
name: e.function.name,
|
|
32
|
+
args: v(e.function.arguments)
|
|
33
|
+
});
|
|
34
|
+
if (n.role === "tool" && n.tool_call_id) {
|
|
35
|
+
let e = t.get(n.tool_call_id);
|
|
36
|
+
e && (e.result = n.content);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return t;
|
|
40
|
+
}, b = (e) => {
|
|
41
|
+
let t = /* @__PURE__ */ new Set();
|
|
42
|
+
for (let n of e) for (let e of y(n.custom_content?.state).values()) t.add(e.name);
|
|
43
|
+
return t;
|
|
44
|
+
}, x = (e, t, n) => {
|
|
45
|
+
if (t.length === 0 || e.role !== m.Assistant) return;
|
|
46
|
+
let r = new Set([...y(e.custom_content?.state).values()].map((e) => e.name));
|
|
47
|
+
return t.find((e) => r.has(e.toolName)) || t.find((e) => e.discovery === "direct") || (n ? t[0] : void 0);
|
|
48
|
+
}, S = (e) => `${e}:mcp-app`, C = (e) => e == null ? void 0 : JSON.stringify(e.toolInput ?? null), w = (e, t) => {
|
|
49
|
+
let n = e.custom_content?.state;
|
|
50
|
+
if (!n) return;
|
|
51
|
+
let r = [...y(n).values()].reverse().find((e) => e.name === t);
|
|
52
|
+
if (r) return {
|
|
53
|
+
toolInput: r.args,
|
|
54
|
+
toolResult: r.result == null ? void 0 : { content: [{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: r.result
|
|
57
|
+
}] }
|
|
58
|
+
};
|
|
59
|
+
}, T = async (e, t, n) => {
|
|
60
|
+
if (!(e.discovery === "direct" || t?.toolInput != null)) return t?.toolResult;
|
|
61
|
+
try {
|
|
62
|
+
return await n(e.toolsetId, e.mcpToolName, t?.toolInput ?? {}, e.kind);
|
|
63
|
+
} catch {
|
|
64
|
+
return t?.toolResult;
|
|
65
|
+
}
|
|
66
|
+
}, E = (t, n, r, i, a) => {
|
|
67
|
+
let { hostContext: o, sandboxUrl: s, fetchResourceHtml: c, callTool: d } = a, [f, m] = p(), [h, g] = p(), [v, y] = p(() => t == null || s == null ? _.Unavailable : _.Loading), [b, x] = p(0), S = l(() => {
|
|
68
|
+
r.invalidate(i), x((e) => e + 1);
|
|
69
|
+
}, [r, i]), w = l(async (e) => {
|
|
70
|
+
if (!(t == null || s == null)) try {
|
|
71
|
+
let a = C(n), { html: o, toolResult: s } = await r.getOrFetch(i, a, async () => {
|
|
72
|
+
let [e, r] = await Promise.all([c(t.toolsetId, t.resourceUri), T(t, n, d)]);
|
|
73
|
+
return {
|
|
74
|
+
html: e,
|
|
75
|
+
toolResult: r
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
if (e()) return;
|
|
79
|
+
m(o), g(s), y(_.Ready);
|
|
80
|
+
} catch {
|
|
81
|
+
e() || y(_.Error);
|
|
82
|
+
}
|
|
83
|
+
}, [
|
|
84
|
+
t,
|
|
85
|
+
n,
|
|
86
|
+
s,
|
|
87
|
+
r,
|
|
88
|
+
i,
|
|
89
|
+
c,
|
|
90
|
+
d
|
|
91
|
+
]);
|
|
92
|
+
u(() => {
|
|
93
|
+
if (t == null || s == null) {
|
|
94
|
+
y(_.Unavailable);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let e = !1;
|
|
98
|
+
return y(_.Loading), w(() => e), () => {
|
|
99
|
+
e = !0;
|
|
100
|
+
};
|
|
101
|
+
}, [
|
|
102
|
+
t,
|
|
103
|
+
s,
|
|
104
|
+
w,
|
|
105
|
+
b
|
|
106
|
+
]);
|
|
107
|
+
let E = l(async (e, n) => {
|
|
108
|
+
if (t == null) throw Error(`Tool call "${e}" failed: no matching MCP App`);
|
|
109
|
+
return d(t.toolsetId, e, n, t.kind);
|
|
110
|
+
}, [t, d]);
|
|
111
|
+
return v !== _.Ready || t == null || s == null || f == null ? {
|
|
112
|
+
status: v,
|
|
113
|
+
reload: S
|
|
114
|
+
} : {
|
|
115
|
+
status: v,
|
|
116
|
+
reload: S,
|
|
117
|
+
content: {
|
|
118
|
+
type: e.McpApp,
|
|
119
|
+
html: f,
|
|
120
|
+
sandboxUrl: s,
|
|
121
|
+
toolName: t.mcpToolName,
|
|
122
|
+
toolInput: n?.toolInput,
|
|
123
|
+
toolResult: h,
|
|
124
|
+
hostContext: o,
|
|
125
|
+
onToolCall: E
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}, D = c(({ match: e, toolCall: c, cache: l, cacheKey: u, hostAdapter: d, onExpand: f, expandAriaLabel: p, reloadAriaLabel: m, loadErrorLabel: v }) => {
|
|
129
|
+
let { status: y, content: b, reload: x } = E(e, c, l, u, d);
|
|
130
|
+
return y === _.Unavailable ? null : /* @__PURE__ */ g("div", {
|
|
131
|
+
className: "bg-layer-2 flex w-full flex-col overflow-hidden rounded border border-tertiary",
|
|
132
|
+
children: [/* @__PURE__ */ g("div", {
|
|
133
|
+
className: "flex items-center justify-end gap-1 border-b border-tertiary px-3 py-1.5",
|
|
134
|
+
children: [/* @__PURE__ */ h(r, {
|
|
135
|
+
icon: /* @__PURE__ */ h(s, {
|
|
136
|
+
size: 16,
|
|
137
|
+
stroke: n,
|
|
138
|
+
"aria-hidden": !0
|
|
139
|
+
}),
|
|
140
|
+
"aria-label": m,
|
|
141
|
+
onClick: x
|
|
142
|
+
}), /* @__PURE__ */ h(r, {
|
|
143
|
+
icon: /* @__PURE__ */ h(o, {
|
|
144
|
+
size: 16,
|
|
145
|
+
stroke: n,
|
|
146
|
+
"aria-hidden": !0
|
|
147
|
+
}),
|
|
148
|
+
"aria-label": p,
|
|
149
|
+
onClick: f
|
|
150
|
+
})]
|
|
151
|
+
}), /* @__PURE__ */ g("div", {
|
|
152
|
+
className: "relative min-h-[200px] w-full",
|
|
153
|
+
children: [
|
|
154
|
+
y === _.Loading && /* @__PURE__ */ h("div", {
|
|
155
|
+
className: "absolute inset-0 flex items-center justify-center",
|
|
156
|
+
children: /* @__PURE__ */ h(i, {})
|
|
157
|
+
}),
|
|
158
|
+
y === _.Error && /* @__PURE__ */ g("div", {
|
|
159
|
+
className: "absolute inset-0 flex flex-col items-center justify-center gap-2 p-4",
|
|
160
|
+
children: [/* @__PURE__ */ h(a, {
|
|
161
|
+
size: 40,
|
|
162
|
+
stroke: n,
|
|
163
|
+
"aria-hidden": !0,
|
|
164
|
+
className: "text-error"
|
|
165
|
+
}), /* @__PURE__ */ h("p", {
|
|
166
|
+
role: "alert",
|
|
167
|
+
className: "dial-body-text text-center text-primary",
|
|
168
|
+
children: v
|
|
169
|
+
})]
|
|
170
|
+
}),
|
|
171
|
+
y === _.Ready && b && /* @__PURE__ */ h(t, {
|
|
172
|
+
content: b,
|
|
173
|
+
errorLabel: v
|
|
174
|
+
})
|
|
175
|
+
]
|
|
176
|
+
})]
|
|
177
|
+
});
|
|
178
|
+
}), O = 900 * 1e3, k = (e) => {
|
|
179
|
+
let t = f(/* @__PURE__ */ new Map()), n = f(/* @__PURE__ */ new Map());
|
|
180
|
+
return d(() => {
|
|
181
|
+
let r = (t) => `${e}:${t}`, i = (e, t) => `${r(e)}::${t ?? ""}`, a = (e, n) => {
|
|
182
|
+
let i = t.current.get(r(e));
|
|
183
|
+
if (i != null && i.seedKey === n && !(Date.now() - i.cachedAt > O)) return {
|
|
184
|
+
html: i.html,
|
|
185
|
+
toolResult: i.toolResult
|
|
186
|
+
};
|
|
187
|
+
}, o = (e, n, i) => {
|
|
188
|
+
let a = Date.now();
|
|
189
|
+
for (let [e, n] of t.current) a - n.cachedAt > O && t.current.delete(e);
|
|
190
|
+
t.current.set(r(e), {
|
|
191
|
+
...n,
|
|
192
|
+
seedKey: i,
|
|
193
|
+
cachedAt: a
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
get: a,
|
|
198
|
+
set: o,
|
|
199
|
+
invalidate: (e) => {
|
|
200
|
+
t.current.delete(r(e));
|
|
201
|
+
let i = `${r(e)}::`;
|
|
202
|
+
for (let e of n.current.keys()) e.startsWith(i) && n.current.delete(e);
|
|
203
|
+
},
|
|
204
|
+
getOrFetch: (e, t, r) => {
|
|
205
|
+
let s = a(e, t);
|
|
206
|
+
if (s) return Promise.resolve(s);
|
|
207
|
+
let c = i(e, t), l = n.current.get(c);
|
|
208
|
+
return l || (l = r().then((n) => (o(e, n, t), n)), n.current.set(c, l), l.finally(() => {
|
|
209
|
+
n.current.delete(c);
|
|
210
|
+
})), l;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}, [e]);
|
|
214
|
+
};
|
|
215
|
+
//#endregion
|
|
216
|
+
export { D as McpAppInlinePreview, _ as McpAppInlinePreviewStatus, b as collectToolCallNames, C as computeMcpAppSeedKey, x as findMcpAppForMessage, S as mcpAppCanvasKey, w as resolveMcpAppToolCallSeed, T as resolveMcpAppToolResult, E as useMcpAppInlinePreview, k as useMcpAppResponseCache };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { McpUiHostContext } from '@mcp-ui/client';
|
|
2
|
+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
/** Which of the host's two MCP proxy routes a deployment id resolves through — a plain toolset, or an application that is itself an MCP server. */
|
|
4
|
+
export type McpDeploymentKind = 'toolset' | 'application';
|
|
5
|
+
/**
|
|
6
|
+
* How a tool was discovered — determines whether it's safe to live-re-call
|
|
7
|
+
* (see `resolveMcpAppToolResult`) or treat as "always available" for a
|
|
8
|
+
* message with no other evidence (see `findMcpAppForMessage`).
|
|
9
|
+
*
|
|
10
|
+
* - `'direct'`: the active deployment itself IS this MCP server (whether
|
|
11
|
+
* it's a bare Toolset or an Application declaring `features.mcp`) — the
|
|
12
|
+
* host is talking to it directly, so which tool a mounted app's own
|
|
13
|
+
* invocation targets is unambiguous.
|
|
14
|
+
* - `'indirect'`: a different, tool-calling deployment's message happened to
|
|
15
|
+
* call a name prefixed with a toolset's display name; the toolset is a
|
|
16
|
+
* name-prefix *guess*, not a confirmed link the host was told about, so
|
|
17
|
+
* re-calling it live risks hitting the wrong tool or re-triggering a
|
|
18
|
+
* non-idempotent side effect.
|
|
19
|
+
*/
|
|
20
|
+
export type McpAppToolDiscovery = 'direct' | 'indirect';
|
|
21
|
+
/** A tool's declared MCP Apps UI resource, keyed by the toolset it was discovered on. */
|
|
22
|
+
export interface McpAppToolRef {
|
|
23
|
+
/** Id of the toolset/application this tool was discovered on. */
|
|
24
|
+
toolsetId: string;
|
|
25
|
+
/** The tool's declared `ui://` resource. */
|
|
26
|
+
resourceUri: string;
|
|
27
|
+
/** Name used to correlate this ref against a message's real tool-call data (`resolveMcpAppToolCallSeed`/`findMcpAppForMessage`). */
|
|
28
|
+
toolName: string;
|
|
29
|
+
/** The tool's real name as declared by its owning toolset's `tools/list` — what must be passed to the mounted app/`onToolCall`. */
|
|
30
|
+
mcpToolName: string;
|
|
31
|
+
/** Deployment kind — determines which host MCP proxy route resolves `tools/call` for this tool. */
|
|
32
|
+
kind: McpDeploymentKind;
|
|
33
|
+
/** How this tool was discovered — see `McpAppToolDiscovery`. */
|
|
34
|
+
discovery: McpAppToolDiscovery;
|
|
35
|
+
}
|
|
36
|
+
/** Original tool call's arguments/result, seeded into the mounted app so it renders that invocation immediately instead of an empty initial state. */
|
|
37
|
+
export interface McpAppToolCallSeed {
|
|
38
|
+
/** The tool call's real structured arguments. */
|
|
39
|
+
toolInput?: Record<string, unknown>;
|
|
40
|
+
/** The tool call's result, if already known. */
|
|
41
|
+
toolResult?: CallToolResult;
|
|
42
|
+
}
|
|
43
|
+
/** Host-provided function that fetches a toolset's MCP Apps `ui://` resource and resolves to its HTML body. */
|
|
44
|
+
export type FetchMcpAppResourceHtml = (toolsetId: string, resourceUri: string) => Promise<string>;
|
|
45
|
+
/** Host-provided function that forwards an MCP App's tool call through the host's backend. */
|
|
46
|
+
export type CallMcpAppTool = (toolsetId: string, toolName: string, args: unknown, kind: McpDeploymentKind) => Promise<CallToolResult>;
|
|
47
|
+
/** A cached MCP App resource fetch plus its resolved tool result. */
|
|
48
|
+
export interface CachedMcpAppResponse {
|
|
49
|
+
/** The fetched resource's HTML body. */
|
|
50
|
+
html: string;
|
|
51
|
+
/** The tool call's resolved result, if any. */
|
|
52
|
+
toolResult?: CallToolResult;
|
|
53
|
+
}
|
|
54
|
+
/** Reads/writes `useMcpAppResponseCache`'s per-conversation cache. */
|
|
55
|
+
export interface McpAppResponseCache {
|
|
56
|
+
/**
|
|
57
|
+
* Returns the cached entry only if it was written for this exact
|
|
58
|
+
* `seedKey` (`computeMcpAppSeedKey`) and is younger than the cache's TTL;
|
|
59
|
+
* otherwise `undefined` (a miss), so a stale or seed-mismatched entry is
|
|
60
|
+
* never served.
|
|
61
|
+
*/
|
|
62
|
+
get: (key: string, seedKey: string | undefined) => CachedMcpAppResponse | undefined;
|
|
63
|
+
/** Writes an entry for `key`, tagged with the seed it was resolved from. */
|
|
64
|
+
set: (key: string, value: CachedMcpAppResponse, seedKey: string | undefined) => void;
|
|
65
|
+
/** Removes the cached entry for `key`, if any. */
|
|
66
|
+
invalidate: (key: string) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Returns the fresh cached entry for `(key, seedKey)` if one exists
|
|
69
|
+
* (same rules as `get`); otherwise runs `fetchFn` and caches its result.
|
|
70
|
+
* Concurrent calls for the same `(key, seedKey)` before `fetchFn` settles
|
|
71
|
+
* are coalesced onto the same in-flight promise instead of each starting
|
|
72
|
+
* their own — without this, a component re-rendered before its first
|
|
73
|
+
* fetch resolves (e.g. React StrictMode's dev-only double effect
|
|
74
|
+
* invocation) would race two independent fetches/live tool re-calls for
|
|
75
|
+
* the same message.
|
|
76
|
+
*/
|
|
77
|
+
getOrFetch: (key: string, seedKey: string | undefined, fetchFn: () => Promise<CachedMcpAppResponse>) => Promise<CachedMcpAppResponse>;
|
|
78
|
+
}
|
|
79
|
+
/** Load state of `useMcpAppInlinePreview`'s fetch. */
|
|
80
|
+
export declare enum McpAppInlinePreviewStatus {
|
|
81
|
+
Loading = "loading",
|
|
82
|
+
Ready = "ready",
|
|
83
|
+
Error = "error",
|
|
84
|
+
Unavailable = "unavailable"
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Dependencies the host must inject into `useMcpAppInlinePreview` /
|
|
88
|
+
* `McpAppInlinePreview` — everything that requires host context (theming,
|
|
89
|
+
* locale, the configured API client) rather than being derivable from the
|
|
90
|
+
* hook's own arguments.
|
|
91
|
+
*/
|
|
92
|
+
export interface McpAppHostAdapter {
|
|
93
|
+
/** `McpUiHostContext` to deliver to the mounted app's `ui/initialize` handshake. */
|
|
94
|
+
hostContext: McpUiHostContext;
|
|
95
|
+
/** Operator-configured MCP Apps sandbox-proxy URL, or `null` when unconfigured. */
|
|
96
|
+
sandboxUrl: string | null;
|
|
97
|
+
/** Fetches the matched tool's MCP Apps `ui://` resource. */
|
|
98
|
+
fetchResourceHtml: FetchMcpAppResourceHtml;
|
|
99
|
+
/** Forwards a tool call the mounted app makes on its own (`onToolCall`) or the live re-call `resolveMcpAppToolResult` performs. */
|
|
100
|
+
callTool: CallMcpAppTool;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=mcp-apps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-apps.d.ts","sourceRoot":"","sources":["../../src/models/mcp-apps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,mJAAmJ;AACnJ,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,aAAa,CAAC;AAE1D;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,UAAU,CAAC;AAExD,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,oIAAoI;IACpI,QAAQ,EAAE,MAAM,CAAC;IACjB,mIAAmI;IACnI,WAAW,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,IAAI,EAAE,iBAAiB,CAAC;IACxB,gEAAgE;IAChE,SAAS,EAAE,mBAAmB,CAAC;CAChC;AAED,sJAAsJ;AACtJ,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,gDAAgD;IAChD,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,+GAA+G;AAC/G,MAAM,MAAM,uBAAuB,GAAG,CACpC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,KAChB,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,8FAA8F;AAC9F,MAAM,MAAM,cAAc,GAAG,CAC3B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,iBAAiB,KACpB,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,qEAAqE;AACrE,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,GAAG,EAAE,CACH,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GAAG,SAAS,KACxB,oBAAoB,GAAG,SAAS,CAAC;IACtC,4EAA4E;IAC5E,GAAG,EAAE,CACH,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,oBAAoB,EAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,KACxB,IAAI,CAAC;IACV,kDAAkD;IAClD,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;;;;;;;OASG;IACH,UAAU,EAAE,CACV,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,KACzC,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACpC;AAED,sDAAsD;AACtD,oBAAY,yBAAyB;IACnC,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,KAAK,UAAU;IACf,WAAW,gBAAgB;CAC5B;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,oFAAoF;IACpF,WAAW,EAAE,gBAAgB,CAAC;IAC9B,mFAAmF;IACnF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,4DAA4D;IAC5D,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C,mIAAmI;IACnI,QAAQ,EAAE,cAAc,CAAC;CAC1B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@epam/ai-dial-mcp-apps",
|
|
3
|
+
"description": "Host-agnostic MCP Apps building blocks — tool-call/message matching, the response cache, and the inline preview hook/component shared by the full-width canvas and inline surfaces",
|
|
4
|
+
"version": "1.1.0-dev.405",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./index.js",
|
|
8
|
+
"module": "./index.js",
|
|
9
|
+
"types": "./index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
"./package.json": "./package.json",
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"import": "./index.js",
|
|
15
|
+
"default": "./index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@epam/ai-dial-attachment-canvas": "1.1.0-dev.405",
|
|
20
|
+
"@epam/ai-dial-chat-shared": "1.1.0-dev.405",
|
|
21
|
+
"@epam/ai-dial-ui-kit": "^0.14.0-dev.15",
|
|
22
|
+
"@mcp-ui/client": "^7.1.1",
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
24
|
+
"@tabler/icons-react": "^3.44.0",
|
|
25
|
+
"react": "^19.0.0"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/epam/ai-dial-chat.git",
|
|
30
|
+
"directory": "libs/mcp-apps"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Message } from '@epam/ai-dial-chat-shared';
|
|
2
|
+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { CallMcpAppTool, McpAppToolCallSeed, McpAppToolRef } from '../models/mcp-apps';
|
|
4
|
+
/**
|
|
5
|
+
* Collects every real tool-call name seen across `messages`' `custom_content.state`
|
|
6
|
+
* (in whichever known orchestrator shape is present). Used to discover an MCP-capable
|
|
7
|
+
* toolset that a non-MCP application delegates to internally.
|
|
8
|
+
*/
|
|
9
|
+
export declare const collectToolCallNames: (messages: Message[]) => Set<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Returns the `mcpAppTools` entry that best matches this message: the one
|
|
12
|
+
* whose name was actually called (per `custom_content.state`, in whichever
|
|
13
|
+
* of its two known orchestrator-specific shapes is present).
|
|
14
|
+
*
|
|
15
|
+
* If no real call matches, a `discovery: 'direct'` tool (the deployment
|
|
16
|
+
* itself IS this MCP server, whether it's a bare Toolset or an Application —
|
|
17
|
+
* see `useMcpAppTools`) always wins regardless: such a deployment's own
|
|
18
|
+
* response IS its MCP App responding, and it typically never populates
|
|
19
|
+
* `custom_content.state` at all — it isn't an LLM-orchestrated tool-call
|
|
20
|
+
* turn, so there is no call to find evidence of in the first place (the same
|
|
21
|
+
* class of self-hosted-app gap `design.md`'s D9 "Known gap" documents for
|
|
22
|
+
* discovery — here it recurs at per-message matching).
|
|
23
|
+
*
|
|
24
|
+
* Otherwise (a `discovery: 'indirect'` match — a real MCP-capable toolset a
|
|
25
|
+
* different, tool-calling deployment delegates to, guessed by name-prefix), a
|
|
26
|
+
* real match is required once the message has settled; while still
|
|
27
|
+
* streaming, `custom_content.state` may not have caught up with a tool call
|
|
28
|
+
* the model already made this turn, so the first such tool is guessed
|
|
29
|
+
* optimistically rather than popping in a beat late. Once settled, a
|
|
30
|
+
* deployment that finished without calling any MCP-capable toolset must
|
|
31
|
+
* never show the trigger, even though the deployment supports MCP Apps in
|
|
32
|
+
* general.
|
|
33
|
+
*
|
|
34
|
+
* Returns `undefined` for non-assistant messages or when the deployment has
|
|
35
|
+
* no MCP-Apps-capable tool at all.
|
|
36
|
+
*/
|
|
37
|
+
export declare const findMcpAppForMessage: (message: Message, mcpAppTools: McpAppToolRef[], isStreaming: boolean) => McpAppToolRef | undefined;
|
|
38
|
+
/** Stable key identifying a message's MCP App canvas in an attachment canvas's `attachmentId` tracking, mirroring the `${messageIndex}:${attachmentId}` scheme used for regular attachment tiles. */
|
|
39
|
+
export declare const mcpAppCanvasKey: (messageIndex: number) => string;
|
|
40
|
+
/**
|
|
41
|
+
* Identifies which seed a `useMcpAppResponseCache` entry was resolved from,
|
|
42
|
+
* so a cache lookup can tell a settled tool-call seed apart from the
|
|
43
|
+
* no-tool-call-parsed-yet seed a freshly-streamed message mounts with.
|
|
44
|
+
*/
|
|
45
|
+
export declare const computeMcpAppSeedKey: (toolCall: McpAppToolCallSeed | undefined) => string | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Seeds the mounted app's initial `toolInput`/`toolResult` from the
|
|
48
|
+
* message's real tool-call data (`custom_content.state`, in whichever of its
|
|
49
|
+
* two known orchestrator-specific shapes is present) — `toolInput` is the
|
|
50
|
+
* tool call's real structured arguments; `toolResult` is still a lossy
|
|
51
|
+
* wrapper around the result message's plain-text `content`, since that's
|
|
52
|
+
* still all the orchestrator's state carries (no `_meta` or
|
|
53
|
+
* `structuredContent` survives into it). When the same tool was called more
|
|
54
|
+
* than once this turn (e.g. the model retried after a failed first attempt
|
|
55
|
+
* with corrected arguments), the **last** matching call is used — an earlier
|
|
56
|
+
* attempt's failure is superseded, not authoritative. Returns `undefined` if
|
|
57
|
+
* no matching tool call is found for `toolName`.
|
|
58
|
+
*/
|
|
59
|
+
export declare const resolveMcpAppToolCallSeed: (message: Message, toolName: string) => McpAppToolCallSeed | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the `toolResult` an MCP App canvas should be seeded with. Prefers
|
|
62
|
+
* a live re-call of the tool (through `callTool`) over `seed.toolResult`'s
|
|
63
|
+
* lossy plain-text reconstruction, since a host's conversation state
|
|
64
|
+
* typically never carries the tool's real `structuredContent` — only the
|
|
65
|
+
* orchestrator's flattened prose summary, which is often not enough for the
|
|
66
|
+
* app's UI to render anything at all.
|
|
67
|
+
*
|
|
68
|
+
* Attempted whenever the call is confirmed safe:
|
|
69
|
+
* - `match.discovery === 'direct'` — the deployment the host is talking to
|
|
70
|
+
* IS this MCP server (whether a bare Toolset or an Application), so
|
|
71
|
+
* `toolsetId`/`mcpToolName`/`kind` are unambiguous. Called even when `seed`
|
|
72
|
+
* has no `toolInput`, since a self-hosting MCP App never populates
|
|
73
|
+
* `custom_content.state` in the first place (see `findMcpAppForMessage`) —
|
|
74
|
+
* `{}` is passed instead, the same "give me your current/initial state"
|
|
75
|
+
* call the app's own resource would make on `ui/initialize`.
|
|
76
|
+
* - `seed?.toolInput != null` — a `discovery: 'indirect'` match (the toolset
|
|
77
|
+
* a *different*, tool-calling deployment appears to delegate to) whose
|
|
78
|
+
* `toolName` was matched against a *real* tool call actually seen in
|
|
79
|
+
* `custom_content.state` (`findMcpAppForMessage`'s name-match branch, not
|
|
80
|
+
* its streaming-optimistic `mcpAppTools[0]` guess). Once a real call is
|
|
81
|
+
* confirmed, re-calling it with the exact same `toolsetId`/args it was
|
|
82
|
+
* really invoked with carries no more risk than the direct case.
|
|
83
|
+
*
|
|
84
|
+
* Only the streaming-optimistic guess (`discovery: 'indirect'` with no real
|
|
85
|
+
* call seen yet, so `seed` is `undefined`) is skipped — that toolset/tool
|
|
86
|
+
* pairing is still an unconfirmed guess, and re-calling it could hit the
|
|
87
|
+
* wrong tool or re-trigger a non-idempotent side effect; it keeps
|
|
88
|
+
* `seed?.toolResult` (`undefined` here) as-is. Falls back to
|
|
89
|
+
* `seed?.toolResult` if an attempted live call fails (e.g. the deployment
|
|
90
|
+
* has no live MCP session outside the original conversation turn).
|
|
91
|
+
*/
|
|
92
|
+
export declare const resolveMcpAppToolResult: (match: McpAppToolRef, seed: McpAppToolCallSeed | undefined, callTool: CallMcpAppTool) => Promise<CallToolResult | undefined>;
|
|
93
|
+
//# sourceMappingURL=mcp-app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-app.d.ts","sourceRoot":"","sources":["../../src/utils/mcp-app.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EAEb,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,aAAa,EACd,MAAM,oBAAoB,CAAC;AA2D5B;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAU,OAAO,EAAE,KAAG,GAAG,CAAC,MAAM,CAUpE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAS,OAAO,EAChB,aAAa,aAAa,EAAE,EAC5B,aAAa,OAAO,KACnB,aAAa,GAAG,SAclB,CAAC;AAEF,qMAAqM;AACrM,eAAO,MAAM,eAAe,GAAI,cAAc,MAAM,KAAG,MAC5B,CAAC;AAE5B;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,UAAU,kBAAkB,GAAG,SAAS,KACvC,MAAM,GAAG,SAC+D,CAAC;AAE5E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACpC,SAAS,OAAO,EAChB,UAAU,MAAM,KACf,kBAAkB,GAAG,SAgBvB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,aAAa,EACpB,MAAM,kBAAkB,GAAG,SAAS,EACpC,UAAU,cAAc,KACvB,OAAO,CAAC,cAAc,GAAG,SAAS,CAgBpC,CAAC"}
|