@mcp-web/app 0.1.0
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/LICENSE +201 -0
- package/dist/create-app.d.ts +160 -0
- package/dist/create-app.d.ts.map +1 -0
- package/dist/create-app.js +94 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/internal.d.ts +12 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +10 -0
- package/dist/mcp-app-context.d.ts +122 -0
- package/dist/mcp-app-context.d.ts.map +1 -0
- package/dist/mcp-app-context.js +140 -0
- package/dist/mcp-app-context.test.d.ts +2 -0
- package/dist/mcp-app-context.test.d.ts.map +1 -0
- package/dist/mcp-app-context.test.js +254 -0
- package/dist/render-mcp-app.d.ts +71 -0
- package/dist/render-mcp-app.d.ts.map +1 -0
- package/dist/render-mcp-app.js +87 -0
- package/dist/use-mcp-app-props.d.ts +116 -0
- package/dist/use-mcp-app-props.d.ts.map +1 -0
- package/dist/use-mcp-app-props.js +158 -0
- package/dist/vite-plugin.d.ts +123 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +450 -0
- package/package.json +53 -0
- package/src/create-app.test.ts +181 -0
- package/src/create-app.ts +213 -0
- package/src/index.ts +87 -0
- package/src/internal.ts +12 -0
- package/src/mcp-app-context.test.tsx +323 -0
- package/src/mcp-app-context.tsx +183 -0
- package/src/render-mcp-app.tsx +141 -0
- package/src/use-mcp-app-props.ts +166 -0
- package/src/vite-plugin.test.ts +110 -0
- package/src/vite-plugin.ts +620 -0
- package/tsconfig.json +32 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook to receive props from the MCP host via the ext-apps protocol.
|
|
3
|
+
*
|
|
4
|
+
* This hook connects to the host (e.g., Claude Desktop) using the
|
|
5
|
+
* `@modelcontextprotocol/ext-apps` JSON-RPC protocol. It listens for
|
|
6
|
+
* `tool-result` notifications, which contain the props returned by the
|
|
7
|
+
* tool handler as JSON in `content[0].text`.
|
|
8
|
+
*
|
|
9
|
+
* Must be called within an `MCPAppProvider` (set up automatically
|
|
10
|
+
* by `renderMCPApp`).
|
|
11
|
+
*
|
|
12
|
+
* @template T - The type of props expected from the handler
|
|
13
|
+
* @returns The props object, or null if not yet received
|
|
14
|
+
*
|
|
15
|
+
* @example Basic Usage
|
|
16
|
+
* ```tsx
|
|
17
|
+
* import { useMCPAppProps } from '@mcp-web/app';
|
|
18
|
+
*
|
|
19
|
+
* interface MyAppProps {
|
|
20
|
+
* title: string;
|
|
21
|
+
* data: number[];
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* function MyApp() {
|
|
25
|
+
* const props = useMCPAppProps<MyAppProps>();
|
|
26
|
+
*
|
|
27
|
+
* if (!props) {
|
|
28
|
+
* return <div>Waiting for data...</div>;
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* return (
|
|
32
|
+
* <div>
|
|
33
|
+
* <h1>{props.title}</h1>
|
|
34
|
+
* <ul>
|
|
35
|
+
* {props.data.map((item, i) => (
|
|
36
|
+
* <li key={i}>{item}</li>
|
|
37
|
+
* ))}
|
|
38
|
+
* </ul>
|
|
39
|
+
* </div>
|
|
40
|
+
* );
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example With Default Props for Development
|
|
45
|
+
* ```tsx
|
|
46
|
+
* function MyApp() {
|
|
47
|
+
* const props = useMCPAppProps<MyAppProps>() ?? {
|
|
48
|
+
* title: 'Development Preview',
|
|
49
|
+
* data: [1, 2, 3],
|
|
50
|
+
* };
|
|
51
|
+
*
|
|
52
|
+
* return <div>{props.title}</div>;
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function useMCPAppProps<T>(): T | null;
|
|
57
|
+
/**
|
|
58
|
+
* Get the ext-apps `App` instance for advanced use cases.
|
|
59
|
+
*
|
|
60
|
+
* This hook provides access to the underlying `App` class from
|
|
61
|
+
* `@modelcontextprotocol/ext-apps`, enabling bidirectional communication
|
|
62
|
+
* with the host (e.g., calling server tools, sending messages).
|
|
63
|
+
*
|
|
64
|
+
* Must be called within an `MCPAppProvider` (set up automatically
|
|
65
|
+
* by `renderMCPApp`).
|
|
66
|
+
*
|
|
67
|
+
* @returns The App state including app instance, connection status, and errors
|
|
68
|
+
*
|
|
69
|
+
* @example Calling a server tool from the app
|
|
70
|
+
* ```tsx
|
|
71
|
+
* import { useMCPApp } from '@mcp-web/app';
|
|
72
|
+
*
|
|
73
|
+
* function MyApp() {
|
|
74
|
+
* const { app, isConnected } = useMCPApp();
|
|
75
|
+
*
|
|
76
|
+
* const handleClick = async () => {
|
|
77
|
+
* if (app) {
|
|
78
|
+
* const result = await app.callServerTool({
|
|
79
|
+
* name: 'update_data',
|
|
80
|
+
* arguments: { key: 'value' },
|
|
81
|
+
* });
|
|
82
|
+
* console.log('Server tool result:', result);
|
|
83
|
+
* }
|
|
84
|
+
* };
|
|
85
|
+
*
|
|
86
|
+
* return <button onClick={handleClick}>Update</button>;
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function useMCPApp(): {
|
|
91
|
+
app: import("@modelcontextprotocol/ext-apps").App | null;
|
|
92
|
+
isConnected: boolean;
|
|
93
|
+
error: Error | null;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Get current MCP App props synchronously.
|
|
97
|
+
*
|
|
98
|
+
* @deprecated Use `useMCPAppProps` hook instead. This function is maintained
|
|
99
|
+
* for backward compatibility but does not work with the ext-apps protocol.
|
|
100
|
+
*
|
|
101
|
+
* @template T - The type of props expected
|
|
102
|
+
* @returns null (ext-apps protocol is async-only)
|
|
103
|
+
*/
|
|
104
|
+
export declare function getMCPAppProps<T>(): T | null;
|
|
105
|
+
/**
|
|
106
|
+
* Subscribe to MCP App props changes.
|
|
107
|
+
*
|
|
108
|
+
* @deprecated Use `useMCPAppProps` hook instead. This function is maintained
|
|
109
|
+
* for backward compatibility but does not work with the ext-apps protocol.
|
|
110
|
+
*
|
|
111
|
+
* @template T - The type of props expected
|
|
112
|
+
* @param _listener - Function called when props are received or updated
|
|
113
|
+
* @returns No-op unsubscribe function
|
|
114
|
+
*/
|
|
115
|
+
export declare function subscribeMCPAppProps<T>(_listener: (props: T) => void): () => void;
|
|
116
|
+
//# sourceMappingURL=use-mcp-app-props.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-mcp-app-props.d.ts","sourceRoot":"","sources":["../src/use-mcp-app-props.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,cAAc,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAwC5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,SAAS;;;;EAGxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAE5C;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAC5B,MAAM,IAAI,CAEZ"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useMCPAppContext } from './mcp-app-context.js';
|
|
3
|
+
/**
|
|
4
|
+
* React hook to receive props from the MCP host via the ext-apps protocol.
|
|
5
|
+
*
|
|
6
|
+
* This hook connects to the host (e.g., Claude Desktop) using the
|
|
7
|
+
* `@modelcontextprotocol/ext-apps` JSON-RPC protocol. It listens for
|
|
8
|
+
* `tool-result` notifications, which contain the props returned by the
|
|
9
|
+
* tool handler as JSON in `content[0].text`.
|
|
10
|
+
*
|
|
11
|
+
* Must be called within an `MCPAppProvider` (set up automatically
|
|
12
|
+
* by `renderMCPApp`).
|
|
13
|
+
*
|
|
14
|
+
* @template T - The type of props expected from the handler
|
|
15
|
+
* @returns The props object, or null if not yet received
|
|
16
|
+
*
|
|
17
|
+
* @example Basic Usage
|
|
18
|
+
* ```tsx
|
|
19
|
+
* import { useMCPAppProps } from '@mcp-web/app';
|
|
20
|
+
*
|
|
21
|
+
* interface MyAppProps {
|
|
22
|
+
* title: string;
|
|
23
|
+
* data: number[];
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* function MyApp() {
|
|
27
|
+
* const props = useMCPAppProps<MyAppProps>();
|
|
28
|
+
*
|
|
29
|
+
* if (!props) {
|
|
30
|
+
* return <div>Waiting for data...</div>;
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* return (
|
|
34
|
+
* <div>
|
|
35
|
+
* <h1>{props.title}</h1>
|
|
36
|
+
* <ul>
|
|
37
|
+
* {props.data.map((item, i) => (
|
|
38
|
+
* <li key={i}>{item}</li>
|
|
39
|
+
* ))}
|
|
40
|
+
* </ul>
|
|
41
|
+
* </div>
|
|
42
|
+
* );
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example With Default Props for Development
|
|
47
|
+
* ```tsx
|
|
48
|
+
* function MyApp() {
|
|
49
|
+
* const props = useMCPAppProps<MyAppProps>() ?? {
|
|
50
|
+
* title: 'Development Preview',
|
|
51
|
+
* data: [1, 2, 3],
|
|
52
|
+
* };
|
|
53
|
+
*
|
|
54
|
+
* return <div>{props.title}</div>;
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function useMCPAppProps() {
|
|
59
|
+
const [props, setProps] = useState(null);
|
|
60
|
+
const { app } = useMCPAppContext();
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (!app)
|
|
63
|
+
return;
|
|
64
|
+
// Listen for tool result - this is where our props come from.
|
|
65
|
+
// The tool handler returns props which the bridge wraps into
|
|
66
|
+
// CallToolResult.content[0].text as JSON.
|
|
67
|
+
app.ontoolresult = async (result) => {
|
|
68
|
+
if (result?.content) {
|
|
69
|
+
for (const block of result.content) {
|
|
70
|
+
if (block.type === 'text' && typeof block.text === 'string') {
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(block.text);
|
|
73
|
+
setProps(parsed);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// If JSON parsing fails, treat the text as-is
|
|
77
|
+
setProps(block.text);
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
// Also listen for tool input - useful for apps that need
|
|
85
|
+
// the raw tool call arguments
|
|
86
|
+
app.ontoolinput = async (input) => {
|
|
87
|
+
// If we have arguments and no result yet, use them as initial props.
|
|
88
|
+
// This enables apps to render immediately with the tool input
|
|
89
|
+
// before the full result arrives.
|
|
90
|
+
if (input?.arguments) {
|
|
91
|
+
setProps((prev) => prev ?? input.arguments);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}, [app]);
|
|
95
|
+
return props;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get the ext-apps `App` instance for advanced use cases.
|
|
99
|
+
*
|
|
100
|
+
* This hook provides access to the underlying `App` class from
|
|
101
|
+
* `@modelcontextprotocol/ext-apps`, enabling bidirectional communication
|
|
102
|
+
* with the host (e.g., calling server tools, sending messages).
|
|
103
|
+
*
|
|
104
|
+
* Must be called within an `MCPAppProvider` (set up automatically
|
|
105
|
+
* by `renderMCPApp`).
|
|
106
|
+
*
|
|
107
|
+
* @returns The App state including app instance, connection status, and errors
|
|
108
|
+
*
|
|
109
|
+
* @example Calling a server tool from the app
|
|
110
|
+
* ```tsx
|
|
111
|
+
* import { useMCPApp } from '@mcp-web/app';
|
|
112
|
+
*
|
|
113
|
+
* function MyApp() {
|
|
114
|
+
* const { app, isConnected } = useMCPApp();
|
|
115
|
+
*
|
|
116
|
+
* const handleClick = async () => {
|
|
117
|
+
* if (app) {
|
|
118
|
+
* const result = await app.callServerTool({
|
|
119
|
+
* name: 'update_data',
|
|
120
|
+
* arguments: { key: 'value' },
|
|
121
|
+
* });
|
|
122
|
+
* console.log('Server tool result:', result);
|
|
123
|
+
* }
|
|
124
|
+
* };
|
|
125
|
+
*
|
|
126
|
+
* return <button onClick={handleClick}>Update</button>;
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export function useMCPApp() {
|
|
131
|
+
const { app, isConnected, error } = useMCPAppContext();
|
|
132
|
+
return { app, isConnected, error };
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Get current MCP App props synchronously.
|
|
136
|
+
*
|
|
137
|
+
* @deprecated Use `useMCPAppProps` hook instead. This function is maintained
|
|
138
|
+
* for backward compatibility but does not work with the ext-apps protocol.
|
|
139
|
+
*
|
|
140
|
+
* @template T - The type of props expected
|
|
141
|
+
* @returns null (ext-apps protocol is async-only)
|
|
142
|
+
*/
|
|
143
|
+
export function getMCPAppProps() {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Subscribe to MCP App props changes.
|
|
148
|
+
*
|
|
149
|
+
* @deprecated Use `useMCPAppProps` hook instead. This function is maintained
|
|
150
|
+
* for backward compatibility but does not work with the ext-apps protocol.
|
|
151
|
+
*
|
|
152
|
+
* @template T - The type of props expected
|
|
153
|
+
* @param _listener - Function called when props are received or updated
|
|
154
|
+
* @returns No-op unsubscribe function
|
|
155
|
+
*/
|
|
156
|
+
export function subscribeMCPAppProps(_listener) {
|
|
157
|
+
return () => { };
|
|
158
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { UserConfig, UserConfigExport } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* MCP-specific options for building MCP Apps.
|
|
4
|
+
*
|
|
5
|
+
* These options control how app definitions are discovered and where
|
|
6
|
+
* the bundled HTML files are output.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* defineMCPAppsConfig(
|
|
11
|
+
* { plugins: [react()] },
|
|
12
|
+
* {
|
|
13
|
+
* appsConfig: 'src/mcp-apps.ts',
|
|
14
|
+
* outDir: 'dist/apps',
|
|
15
|
+
* silenceOverrideWarnings: true,
|
|
16
|
+
* }
|
|
17
|
+
* );
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface MCPAppOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Path to the apps configuration file (relative to project root).
|
|
23
|
+
* This file should export apps created with `createApp()`.
|
|
24
|
+
*
|
|
25
|
+
* If not specified, will search for:
|
|
26
|
+
* - `src/mcp-apps.ts`
|
|
27
|
+
* - `src/mcp-apps.tsx`
|
|
28
|
+
* - `src/mcp/apps.ts`
|
|
29
|
+
* - `src/mcp/apps.tsx`
|
|
30
|
+
*
|
|
31
|
+
* @default auto-detected
|
|
32
|
+
*/
|
|
33
|
+
appsConfig?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Output directory for bundled app HTML files (relative to project root).
|
|
36
|
+
* @default 'public/mcp-web-apps'
|
|
37
|
+
*/
|
|
38
|
+
outDir?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Silence warnings when MCP-required settings override user-provided values.
|
|
41
|
+
* @default false
|
|
42
|
+
*/
|
|
43
|
+
silenceOverrideWarnings?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Define a Vite configuration for building MCP Apps as single HTML files.
|
|
47
|
+
*
|
|
48
|
+
* This function creates a complete Vite config that auto-discovers app
|
|
49
|
+
* definitions and bundles them into self-contained HTML files. These files
|
|
50
|
+
* can be served as MCP App resources that render inline in AI chat interfaces.
|
|
51
|
+
*
|
|
52
|
+
* **How it works:**
|
|
53
|
+
* 1. Scans for a config file (`src/mcp-apps.ts` or `src/mcp/apps.ts`)
|
|
54
|
+
* 2. Parses `createApp()` calls to find app names and components
|
|
55
|
+
* 3. Auto-generates entry files that render each component
|
|
56
|
+
* 4. Bundles everything into single HTML files
|
|
57
|
+
*
|
|
58
|
+
* **Features:**
|
|
59
|
+
* - No manual entry files needed - just define your apps!
|
|
60
|
+
* - Full Vite config flexibility - pass any standard Vite options
|
|
61
|
+
* - Automatic single-file bundling (JS, CSS, assets all inlined)
|
|
62
|
+
* - Watch mode support for development
|
|
63
|
+
* - PostMessage runtime for receiving props from the host
|
|
64
|
+
*
|
|
65
|
+
* **Critical settings that are always enforced:**
|
|
66
|
+
* - `build.assetsInlineLimit` → Maximum (for inlining)
|
|
67
|
+
* - `build.cssCodeSplit` → false (single CSS bundle)
|
|
68
|
+
* - `base` → './' (relative paths)
|
|
69
|
+
*
|
|
70
|
+
* @param viteConfig - Standard Vite configuration options (plugins, build settings, etc.)
|
|
71
|
+
* @param mcpOptions - MCP-specific options (appsConfig, outDir, silenceOverrideWarnings)
|
|
72
|
+
* @returns A Vite UserConfigExport ready for use in vite.config.ts
|
|
73
|
+
*
|
|
74
|
+
* @example Basic Usage
|
|
75
|
+
* ```typescript
|
|
76
|
+
* // vite.apps.config.ts
|
|
77
|
+
* import react from '@vitejs/plugin-react';
|
|
78
|
+
* import { defineMCPAppsConfig } from '@mcp-web/app/vite';
|
|
79
|
+
*
|
|
80
|
+
* export default defineMCPAppsConfig({
|
|
81
|
+
* plugins: [react()],
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example With Custom Options
|
|
86
|
+
* ```typescript
|
|
87
|
+
* import react from '@vitejs/plugin-react';
|
|
88
|
+
* import { defineMCPAppsConfig } from '@mcp-web/app/vite';
|
|
89
|
+
*
|
|
90
|
+
* export default defineMCPAppsConfig(
|
|
91
|
+
* {
|
|
92
|
+
* plugins: [react()],
|
|
93
|
+
* build: {
|
|
94
|
+
* sourcemap: true,
|
|
95
|
+
* minify: 'terser',
|
|
96
|
+
* },
|
|
97
|
+
* },
|
|
98
|
+
* {
|
|
99
|
+
* appsConfig: 'src/my-apps.ts',
|
|
100
|
+
* outDir: 'dist/apps',
|
|
101
|
+
* }
|
|
102
|
+
* );
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @example Apps Config File (src/mcp-apps.ts)
|
|
106
|
+
* ```typescript
|
|
107
|
+
* import { createApp } from '@mcp-web/app';
|
|
108
|
+
* import { Statistics } from './components/Statistics';
|
|
109
|
+
*
|
|
110
|
+
* export const statisticsApp = createApp({
|
|
111
|
+
* name: 'show_statistics',
|
|
112
|
+
* description: 'Display statistics visualization',
|
|
113
|
+
* component: Statistics,
|
|
114
|
+
* handler: () => ({
|
|
115
|
+
* completionRate: 0.75,
|
|
116
|
+
* totalTasks: 100,
|
|
117
|
+
* }),
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export declare function defineMCPAppsConfig(viteConfig?: UserConfig, mcpOptions?: MCPAppOptions): UserConfigExport;
|
|
122
|
+
export default defineMCPAppsConfig;
|
|
123
|
+
//# sourceMappingURL=vite-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../src/vite-plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,UAAU,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAC;AAGjE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAiZD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2EG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,GAAE,UAAe,EAC3B,UAAU,GAAE,aAAkB,GAC7B,gBAAgB,CAwFlB;AAED,eAAe,mBAAmB,CAAC"}
|