threlte-mcp 1.4.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Raul Contreras
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,306 @@
1
+ # Threlte MCP
2
+
3
+ [![npm version](https://img.shields.io/npm/v/threlte-mcp.svg)](https://www.npmjs.com/package/threlte-mcp)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-blue.svg)](https://modelcontextprotocol.io/)
6
+
7
+ An MCP (Model Context Protocol) server that enables AI agents to inspect and manipulate Three.js/Threlte scenes in real-time.
8
+
9
+ ## โœ… Compatible With
10
+
11
+ - **Claude Desktop** - Anthropic's desktop app
12
+ - **Antigravity** - Google's AI IDE
13
+ - **Claude Code** - CLI tool for Claude
14
+ - **Cursor** - AI-powered code editor
15
+ - **Windsurf** - Codeium's AI IDE
16
+ - **Continue** - VS Code AI extension
17
+ - **Any MCP-compatible client**
18
+
19
+ [See detailed compatibility guide โ†’](./COMPATIBILITY.md)
20
+
21
+ ## Features
22
+
23
+ - ๐Ÿ” **Scene Inspection** - View the full 3D scene hierarchy, find objects by name/type
24
+ - ๐ŸŽฏ **Object Manipulation** - Move, rotate, scale, show/hide objects
25
+ - ๐ŸŽจ **Materials & Assets** - Apply materials, load GLTF models, change environment
26
+ - ๐Ÿงช **Asset Analysis** - Inspect GLTF structure and validate performance
27
+ - dYZ+ **Asset Optimization** - Simplify meshes, compress textures, prune unused data
28
+ - dY"? **Svelte Export** - Generate Threlte/Svelte components from GLTF
29
+ - dYZ? **Camera Presets** - Save, load, and animate camera views
30
+ - โšก **Physics Control** - Add physics bodies, apply impulses, set gravity
31
+ - ๐ŸŽญ **Vibe Presets** - Apply mood presets (cozy, spooky, neon, etc.)
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ npm install threlte-mcp
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ### 1. Install the package
42
+
43
+ ```bash
44
+ npm install threlte-mcp
45
+ ```
46
+
47
+ ### 2. Configure your IDE (one-time setup)
48
+
49
+ ```bash
50
+ npx threlte-mcp setup
51
+ ```
52
+
53
+ This auto-detects your IDE (Antigravity, Cursor, Claude Desktop) and creates the MCP config.
54
+
55
+ ### 3. Add the component to your Threlte app
56
+
57
+ Requires Svelte 5 for the MCPBridge component.
58
+
59
+ ```svelte
60
+ <script>
61
+ import { MCPBridgeComponent } from 'threlte-mcp/client';
62
+ </script>
63
+
64
+ <!-- Add inside your <Canvas> -->
65
+ <MCPBridgeComponent />
66
+ ```
67
+
68
+ **That's it!** The AI can now inspect and manipulate your scene.
69
+
70
+ ---
71
+
72
+ <details>
73
+ <summary>๐Ÿ“‹ Manual Configuration (Alternative)</summary>
74
+
75
+ If `npx threlte-mcp setup` doesn't work for your IDE, manually add to your config:
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "threlte-mcp": {
81
+ "command": "npx",
82
+ "args": ["-y", "threlte-mcp"]
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ Config file locations:
89
+ - **Antigravity**: `~/.gemini/antigravity/mcp_config.json`
90
+ - **Cursor**: `~/.cursor/mcp.json`
91
+ - **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac)
92
+
93
+ </details>
94
+
95
+ <details>
96
+ <summary>๐Ÿ”ง Advanced: TypeScript API</summary>
97
+
98
+ ```typescript
99
+ import { MCPBridge } from 'threlte-mcp/client';
100
+ import { useThrelte } from '@threlte/core';
101
+
102
+ const { scene } = useThrelte();
103
+
104
+ // Create bridge (auto-connects in dev mode)
105
+ const bridge = new MCPBridge(scene, {
106
+ url: 'ws://127.0.0.1:8082', // default
107
+ autoConnect: true, // default in dev
108
+ reconnectDelay: 60000, // 1 minute
109
+ });
110
+
111
+ // In render loop
112
+ bridge.update();
113
+
114
+ // Get scene state
115
+ bridge.getSceneState();
116
+
117
+ // Find objects
118
+ bridge.findObjects({ nameContains: 'player' });
119
+ ```
120
+
121
+ </details>
122
+
123
+
124
+ **Option B: Per-Scene (Simple for prototyping)**
125
+
126
+ ```svelte
127
+ <script lang="ts">
128
+ import { onMount, onDestroy } from 'svelte';
129
+ import { useThrelte } from '@threlte/core';
130
+
131
+ const { scene } = useThrelte();
132
+ let ws: WebSocket | null = null;
133
+
134
+ onMount(() => {
135
+ ws = new WebSocket('ws://localhost:8082');
136
+
137
+ ws.onopen = () => {
138
+ console.log('[MCPBridge] Connected');
139
+ // Send initial scene state
140
+ ws?.send(JSON.stringify({
141
+ type: 'sceneState',
142
+ data: serializeScene(scene)
143
+ }));
144
+ };
145
+
146
+ ws.onmessage = (event) => {
147
+ const command = JSON.parse(event.data);
148
+ handleCommand(command);
149
+ };
150
+ });
151
+
152
+ onDestroy(() => {
153
+ ws?.close();
154
+ });
155
+
156
+ function serializeScene(obj: THREE.Object3D, depth = 0, maxDepth = 3) {
157
+ if (depth > maxDepth) return null;
158
+ return {
159
+ name: obj.name,
160
+ type: obj.type,
161
+ position: obj.position.toArray(),
162
+ rotation: obj.rotation.toArray().slice(0, 3),
163
+ scale: obj.scale.toArray(),
164
+ visible: obj.visible,
165
+ children: obj.children.map(c => serializeScene(c, depth + 1, maxDepth)).filter(Boolean)
166
+ };
167
+ }
168
+
169
+ function handleCommand(cmd: any) {
170
+ const { action, requestId, ...params } = cmd;
171
+ let result;
172
+
173
+ switch (action) {
174
+ case 'getFullSceneState':
175
+ result = { data: serializeScene(scene, 0, params.maxDepth || 3) };
176
+ break;
177
+ case 'moveSceneObject':
178
+ const obj = scene.getObjectByName(params.name || params.path);
179
+ if (obj && params.position) {
180
+ obj.position.set(...params.position);
181
+ result = { success: true };
182
+ }
183
+ break;
184
+ // Add more handlers as needed
185
+ }
186
+
187
+ if (requestId) {
188
+ ws?.send(JSON.stringify({ ...result, requestId }));
189
+ }
190
+ }
191
+ </script>
192
+ ```
193
+
194
+ ### 3. Run your app and start using MCP tools
195
+
196
+ Once both the MCP server and your Threlte app are running, AI agents can:
197
+
198
+ ```
199
+ "Get the scene state"
200
+ "Find all objects named 'Player'"
201
+ "Move the 'Camera' to position [0, 5, 10]"
202
+ "Apply the 'neon' vibe to the scene"
203
+ ```
204
+
205
+ ## Available Tools
206
+
207
+ ### Scene Inspection
208
+ | Tool | Description |
209
+ |------|-------------|
210
+ | `get_scene_state` | Get full scene hierarchy |
211
+ | `find_objects` | Search by name, type, or userData |
212
+ | `get_object_position` | Get position of specific object |
213
+ | `log_positions` | Export positions for code |
214
+
215
+ ### Camera
216
+ | Tool | Description |
217
+ |------|-------------|
218
+ | `set_camera_position` | Set camera position, lookAt, and lens settings |
219
+ | `save_camera_preset` | Save current camera view as a preset |
220
+ | `load_camera_preset` | Load a saved camera view |
221
+ | `list_camera_presets` | List all saved camera presets |
222
+ | `delete_camera_preset` | Delete a saved camera preset |
223
+ | `animate_camera_presets` | Animate through a sequence of presets |
224
+
225
+ ### Hierarchy Management
226
+ | Tool | Description |
227
+ |------|-------------|
228
+ | `spawn_entity` | Create primitive (box, sphere, etc.) |
229
+ | `destroy_entity` | Remove object from scene |
230
+ | `move_object` | Set object position |
231
+ | `set_transform` | Set position, rotation, scale |
232
+ | `set_visibility` | Show/hide object |
233
+ | `rename_entity` | Rename object |
234
+ | `duplicate_entity` | Clone object |
235
+
236
+ ### Physics
237
+ | Tool | Description |
238
+ |------|-------------|
239
+ | `make_physical` | Add physics body |
240
+ | `remove_physics` | Remove physics body |
241
+ | `apply_impulse` | Apply force |
242
+ | `set_gravity` | Set global gravity |
243
+
244
+ ### Asset Processing
245
+ | Tool | Description |
246
+ |------|-------------|
247
+ | `analyze_gltf` | Inspect GLTF/GLB structure |
248
+ | `validate_asset` | Validate GLTF/GLB for issues |
249
+ | `optimize_gltf` | Optimize GLTF/GLB assets |
250
+ | `export_to_svelte` | Generate Threlte/Svelte component |
251
+
252
+ ### Materials & Assets
253
+ | Tool | Description |
254
+ |------|-------------|
255
+ | `load_asset` | Load GLTF/GLB model |
256
+ | `apply_material` | Set material |
257
+ | `set_environment` | Set skybox/environment |
258
+
259
+ ### Atmosphere
260
+ | Tool | Description |
261
+ |------|-------------|
262
+ | `apply_vibe` | Apply mood preset |
263
+ | `get_bridge_status` | Check connection status |
264
+
265
+ ## Configuration
266
+
267
+ ### WebSocket Connection
268
+
269
+ The server uses port 8082 by default for WebSocket communication.
270
+
271
+ ### Environment Variables
272
+
273
+ The MCPBridge auto-connects in development mode. For production, you can enable it via environment variable:
274
+
275
+ ```bash
276
+ # Add to .env file:
277
+ VITE_MCP_ENABLED=true
278
+ ```
279
+
280
+ This is useful for:
281
+ - Testing in production builds
282
+ - Demo environments
283
+ - Debugging production issues
284
+
285
+ **Note:** Auto-enabled in development mode (`npm run dev`), no configuration needed.
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ # Clone the repo
291
+ git clone https://github.com/RaulContreras123/threlte-mcp.git
292
+ cd threlte-mcp
293
+
294
+ # Install dependencies
295
+ npm install
296
+
297
+ # Run in development
298
+ npm run dev
299
+
300
+ # Build for production
301
+ npm run build
302
+ ```
303
+
304
+ ## License
305
+
306
+ MIT ยฉ Raul Contreras
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Threlte MCP - WebSocket Bridge Server
3
+ *
4
+ * Provides a WebSocket server that game clients connect to for real-time
5
+ * scene inspection and manipulation by AI agents.
6
+ */
7
+ export interface MCPCommand {
8
+ action: string;
9
+ requestId?: string;
10
+ maxDepth?: number;
11
+ name?: string;
12
+ path?: string;
13
+ position?: number[];
14
+ rotation?: number[];
15
+ scale?: number[];
16
+ lookAt?: number[];
17
+ fov?: number;
18
+ near?: number;
19
+ far?: number;
20
+ filter?: {
21
+ nameContains?: string;
22
+ type?: string;
23
+ hasUserData?: string;
24
+ };
25
+ id?: string;
26
+ type?: string;
27
+ material?: {
28
+ color?: string;
29
+ metalness?: number;
30
+ roughness?: number;
31
+ };
32
+ parent?: string;
33
+ visible?: boolean;
34
+ newName?: string;
35
+ offset?: number[];
36
+ colliders?: string;
37
+ mass?: number;
38
+ friction?: number;
39
+ restitution?: number;
40
+ gravityScale?: number;
41
+ vector?: number[];
42
+ url?: string;
43
+ preset?: string;
44
+ prop?: string;
45
+ value?: string | number | boolean;
46
+ background?: string;
47
+ color?: string;
48
+ sheet?: string;
49
+ sequence?: string;
50
+ rate?: number;
51
+ range?: [number, number];
52
+ reverse?: boolean;
53
+ time?: number;
54
+ object?: string;
55
+ key?: string;
56
+ event?: string;
57
+ payload?: object;
58
+ questId?: string;
59
+ status?: string;
60
+ progress?: number;
61
+ category?: string;
62
+ vibe?: string;
63
+ targets?: string;
64
+ properties?: string[];
65
+ intensity?: number;
66
+ animate?: boolean;
67
+ duration?: number;
68
+ }
69
+ export interface BridgeServerOptions {
70
+ port?: number;
71
+ commandTimeout?: number;
72
+ }
73
+ export declare class BridgeServer {
74
+ private wss;
75
+ private client;
76
+ private pendingRequests;
77
+ private requestId;
78
+ private lastSceneState;
79
+ private listening;
80
+ private lastError;
81
+ private startupError;
82
+ private port;
83
+ private commandTimeout;
84
+ constructor(options?: BridgeServerOptions);
85
+ private startServer;
86
+ getStatus(): {
87
+ listening: boolean;
88
+ port: number;
89
+ connected: boolean;
90
+ startupError: string | null;
91
+ lastError: string | null;
92
+ pendingRequests: number;
93
+ };
94
+ isConnected(): boolean;
95
+ waitForConnection(timeoutMs?: number): Promise<void>;
96
+ connect(): Promise<void>;
97
+ private handleMessage;
98
+ sendCommand(command: MCPCommand): Promise<unknown>;
99
+ close(): void;
100
+ }
101
+ //# sourceMappingURL=bridge-server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-server.d.ts","sourceRoot":"","sources":["../src/bridge-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Threlte MCP - WebSocket Bridge Server
3
+ *
4
+ * Provides a WebSocket server that game clients connect to for real-time
5
+ * scene inspection and manipulation by AI agents.
6
+ */
7
+ import { WebSocketServer, WebSocket } from 'ws';
8
+ export class BridgeServer {
9
+ wss = null;
10
+ client = null;
11
+ pendingRequests = new Map();
12
+ requestId = 0;
13
+ lastSceneState = null;
14
+ listening = false;
15
+ lastError = null;
16
+ startupError = null;
17
+ port;
18
+ commandTimeout;
19
+ constructor(options = {}) {
20
+ this.port = options.port ?? 8083;
21
+ this.commandTimeout = options.commandTimeout ?? 5000;
22
+ this.startServer();
23
+ }
24
+ startServer() {
25
+ try {
26
+ console.error(`[BridgeServer] Starting WebSocket server on port ${this.port}...`);
27
+ this.wss = new WebSocketServer({ port: this.port });
28
+ this.wss.on('listening', () => {
29
+ console.error(`[BridgeServer] WebSocket server listening on port ${this.port}`);
30
+ this.listening = true;
31
+ this.startupError = null;
32
+ });
33
+ this.wss.on('connection', (ws) => {
34
+ console.error('[BridgeServer] Game client connected');
35
+ this.client = ws;
36
+ ws.on('message', (data) => {
37
+ try {
38
+ const message = JSON.parse(data.toString());
39
+ this.handleMessage(message);
40
+ }
41
+ catch (error) {
42
+ console.error('[BridgeServer] Failed to parse message:', error);
43
+ }
44
+ });
45
+ ws.on('close', () => {
46
+ console.error('[BridgeServer] Game client disconnected');
47
+ this.client = null;
48
+ });
49
+ ws.on('error', (error) => {
50
+ console.error('[BridgeServer] Client error:', error);
51
+ this.lastError = error.message;
52
+ });
53
+ });
54
+ this.wss.on('error', (error) => {
55
+ console.error('[BridgeServer] Server error:', error);
56
+ this.startupError = error.message;
57
+ this.listening = false;
58
+ });
59
+ }
60
+ catch (error) {
61
+ console.error('[BridgeServer] Failed to start server:', error);
62
+ this.startupError = error.message;
63
+ }
64
+ }
65
+ getStatus() {
66
+ return {
67
+ listening: this.listening,
68
+ port: this.port,
69
+ connected: this.client !== null && this.client.readyState === WebSocket.OPEN,
70
+ startupError: this.startupError,
71
+ lastError: this.lastError,
72
+ pendingRequests: this.pendingRequests.size
73
+ };
74
+ }
75
+ isConnected() {
76
+ return this.client !== null && this.client.readyState === WebSocket.OPEN;
77
+ }
78
+ async waitForConnection(timeoutMs = 10000) {
79
+ if (this.isConnected())
80
+ return;
81
+ return new Promise((resolve, reject) => {
82
+ const timeout = setTimeout(() => {
83
+ reject(new Error('Timeout waiting for game client to connect'));
84
+ }, timeoutMs);
85
+ const interval = setInterval(() => {
86
+ if (this.isConnected()) {
87
+ clearTimeout(timeout);
88
+ clearInterval(interval);
89
+ resolve();
90
+ }
91
+ }, 100);
92
+ });
93
+ }
94
+ async connect() {
95
+ return this.waitForConnection();
96
+ }
97
+ handleMessage(message) {
98
+ if (typeof message === 'object' && message !== null && 'data' in message) {
99
+ this.lastSceneState = message;
100
+ }
101
+ if (typeof message === 'object' &&
102
+ message !== null &&
103
+ 'requestId' in message) {
104
+ const { requestId, ...data } = message;
105
+ const pending = this.pendingRequests.get(requestId);
106
+ if (pending) {
107
+ clearTimeout(pending.timeout);
108
+ this.pendingRequests.delete(requestId);
109
+ pending.resolve(data);
110
+ }
111
+ }
112
+ }
113
+ async sendCommand(command) {
114
+ if (!this.isConnected()) {
115
+ throw new Error('No game client connected');
116
+ }
117
+ const requestId = `req_${++this.requestId}`;
118
+ return new Promise((resolve, reject) => {
119
+ const timeout = setTimeout(() => {
120
+ this.pendingRequests.delete(requestId);
121
+ if (command.action === 'getFullSceneState' || command.action === 'findObjects') {
122
+ resolve(this.lastSceneState);
123
+ }
124
+ else {
125
+ reject(new Error('Command timeout'));
126
+ }
127
+ }, this.commandTimeout);
128
+ this.pendingRequests.set(requestId, { resolve, reject, timeout });
129
+ try {
130
+ this.client.send(JSON.stringify({
131
+ ...command,
132
+ requestId,
133
+ }));
134
+ }
135
+ catch (error) {
136
+ clearTimeout(timeout);
137
+ this.pendingRequests.delete(requestId);
138
+ reject(error);
139
+ }
140
+ });
141
+ }
142
+ close() {
143
+ if (this.wss) {
144
+ this.wss.close();
145
+ this.wss = null;
146
+ }
147
+ this.client = null;
148
+ this.pendingRequests.forEach((req) => {
149
+ clearTimeout(req.timeout);
150
+ req.reject(new Error('Server closed'));
151
+ });
152
+ this.pendingRequests.clear();
153
+ }
154
+ }
155
+ //# sourceMappingURL=bridge-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge-server.js","sourceRoot":"","sources":["../src/bridge-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Camera Preset System
3
+ *
4
+ * Manages saving and loading camera positions for quick scene navigation
5
+ * Presets are stored in-memory and can optionally be persisted to localStorage
6
+ */
7
+ export interface CameraPreset {
8
+ name: string;
9
+ position: [number, number, number];
10
+ lookAt?: [number, number, number];
11
+ fov?: number;
12
+ near?: number;
13
+ far?: number;
14
+ timestamp?: number;
15
+ description?: string;
16
+ }
17
+ export interface CameraPresetCollection {
18
+ presets: Record<string, CameraPreset>;
19
+ lastModified: number;
20
+ }
21
+ /**
22
+ * In-memory preset storage
23
+ * Will be synced with browser-side storage via WebSocket
24
+ */
25
+ declare class CameraPresetManager {
26
+ private presets;
27
+ /**
28
+ * Save a camera preset
29
+ */
30
+ savePreset(preset: CameraPreset): void;
31
+ /**
32
+ * Load a camera preset by name
33
+ */
34
+ loadPreset(name: string): CameraPreset | undefined;
35
+ /**
36
+ * List all available presets
37
+ */
38
+ listPresets(): CameraPreset[];
39
+ /**
40
+ * Delete a preset
41
+ */
42
+ deletePreset(name: string): boolean;
43
+ /**
44
+ * Import presets from JSON
45
+ */
46
+ importPresets(data: CameraPresetCollection): void;
47
+ /**
48
+ * Export presets to JSON
49
+ */
50
+ exportPresets(): CameraPresetCollection;
51
+ /**
52
+ * Clear all presets
53
+ */
54
+ clear(): void;
55
+ }
56
+ export declare const cameraPresets: CameraPresetManager;
57
+ /**
58
+ * Default presets for common viewpoints
59
+ */
60
+ export declare const DEFAULT_PRESETS: CameraPreset[];
61
+ export {};
62
+ //# sourceMappingURL=camera-presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-presets.d.ts","sourceRoot":"","sources":["../src/camera-presets.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,cAAM,mBAAmB;IACvB,OAAO,CAAC,OAAO,CAAwC;IAEvD;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAKtC;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIlD;;OAEG;IACH,WAAW,IAAI,YAAY,EAAE;IAM7B;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAInC;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI;IAMjD;;OAEG;IACH,aAAa,IAAI,sBAAsB;IAWvC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAGD,eAAO,MAAM,aAAa,qBAA4B,CAAC;AAEvD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,YAAY,EAwCzC,CAAC"}