figma-console-mcp 1.11.6 β†’ 1.12.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.
Files changed (31) hide show
  1. package/README.md +107 -36
  2. package/dist/cloudflare/core/cloud-websocket-connector.js +246 -0
  3. package/dist/cloudflare/core/cloud-websocket-relay.js +198 -0
  4. package/dist/cloudflare/core/comment-tools.js +3 -3
  5. package/dist/cloudflare/core/console-monitor.js +1 -1
  6. package/dist/cloudflare/core/design-code-tools.js +956 -91
  7. package/dist/cloudflare/core/figma-connector.js +2 -3
  8. package/dist/cloudflare/core/figma-desktop-connector.js +3 -2
  9. package/dist/cloudflare/core/figma-tools.js +22 -22
  10. package/dist/cloudflare/core/port-discovery.js +96 -5
  11. package/dist/cloudflare/core/websocket-connector.js +1 -1
  12. package/dist/cloudflare/core/websocket-server.js +35 -5
  13. package/dist/cloudflare/core/write-tools.js +2006 -0
  14. package/dist/cloudflare/index.js +174 -11
  15. package/dist/core/cloud-websocket-connector.d.ts +58 -0
  16. package/dist/core/cloud-websocket-connector.d.ts.map +1 -0
  17. package/dist/core/cloud-websocket-connector.js +247 -0
  18. package/dist/core/cloud-websocket-connector.js.map +1 -0
  19. package/dist/core/cloud-websocket-relay.d.ts +51 -0
  20. package/dist/core/cloud-websocket-relay.d.ts.map +1 -0
  21. package/dist/core/cloud-websocket-relay.js +171 -0
  22. package/dist/core/cloud-websocket-relay.js.map +1 -0
  23. package/dist/core/write-tools.d.ts +7 -0
  24. package/dist/core/write-tools.d.ts.map +1 -0
  25. package/dist/core/write-tools.js +2007 -0
  26. package/dist/core/write-tools.js.map +1 -0
  27. package/figma-desktop-bridge/README.md +58 -6
  28. package/figma-desktop-bridge/code.js +17 -2
  29. package/figma-desktop-bridge/manifest.json +7 -3
  30. package/figma-desktop-bridge/ui.html +236 -5
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  > **Your design system as an API.** Model Context Protocol server that bridges design and developmentβ€”giving AI assistants complete access to Figma for **extraction**, **creation**, and **debugging**.
10
10
 
11
- > **πŸ†• v1.11.2 β€” Design System Kit for AI Code Generators:** Connect your Figma design system to Lovable, v0, and Replit via the remote MCP endpoint. `figma_get_design_system_kit` extracts tokens, component specs, and resolved styles in one call β€” so AI-generated code builds with your brand. [See changelog β†’](CHANGELOG.md)
11
+ > **πŸ†• Cloud Write Relay β€” Web AI Clients Can Now Design in Figma:** Claude.ai, v0, Replit, and Lovable can now create and modify Figma designs through a cloud relay. No Node.js required β€” just pair your Desktop Bridge plugin with a 6-character code and get 43 tools including full write access. [See changelog β†’](CHANGELOG.md)
12
12
 
13
13
  ## What is this?
14
14
 
@@ -20,7 +20,8 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
20
20
  - **✏️ Design creation** - Create UI components, frames, and layouts directly in Figma
21
21
  - **πŸ”§ Variable management** - Create, update, rename, and delete design tokens
22
22
  - **⚑ Real-time monitoring** - Watch logs as plugins execute
23
- - **πŸ”„ Three ways to install** - Remote SSE (OAuth, zero-setup), NPX (npm package), or Local Git (source code)
23
+ - **☁️ Cloud Write Relay** - Web AI clients (Claude.ai, v0, Replit) can design in Figma via cloud pairing
24
+ - **πŸ”„ Four ways to connect** - Remote SSE, Cloud Mode, NPX, or Local Git
24
25
 
25
26
  ---
26
27
 
@@ -33,21 +34,24 @@ Figma Console MCP connects AI assistants (like Claude) to Figma, enabling:
33
34
  | I want to... | Setup Method | Time |
34
35
  |--------------|--------------|------|
35
36
  | **Create and modify designs with AI** | [NPX Setup](#-npx-setup-recommended) (Recommended) | ~10 min |
37
+ | **Design from the web** (Claude.ai, v0, Replit, Lovable) | [Cloud Mode](#-cloud-mode-web-ai-clients) | ~5 min |
36
38
  | **Contribute to the project** | [Local Git Setup](#for-contributors-local-git-mode) | ~15 min |
37
39
  | **Just explore my design data** (read-only) | [Remote SSE](#-remote-sse-read-only-exploration) | ~2 min |
38
40
 
39
41
  ### ⚠️ Important: Capability Differences
40
42
 
41
- | Capability | NPX / Local Git | Remote SSE |
42
- |------------|-----------------|------------|
43
- | Read design data | βœ… | βœ… |
44
- | **Create components & frames** | βœ… | ❌ |
45
- | **Edit existing designs** | βœ… | ❌ |
46
- | **Manage design tokens/variables** | βœ… | ❌ |
47
- | Desktop Bridge plugin | βœ… | ❌ |
48
- | **Total tools available** | **56+** | **16** |
43
+ | Capability | NPX / Local Git | Cloud Mode | Remote SSE |
44
+ |------------|-----------------|------------|------------|
45
+ | Read design data | βœ… | βœ… | βœ… |
46
+ | **Create components & frames** | βœ… | βœ… | ❌ |
47
+ | **Edit existing designs** | βœ… | βœ… | ❌ |
48
+ | **Manage design tokens/variables** | βœ… | βœ… | ❌ |
49
+ | Real-time monitoring (console, selection) | βœ… | ❌ | ❌ |
50
+ | Desktop Bridge plugin | βœ… | βœ… | ❌ |
51
+ | Requires Node.js | Yes | **No** | No |
52
+ | **Total tools available** | **57+** | **43** | **22** |
49
53
 
50
- > **Bottom line:** Remote SSE is **read-only** with ~34% of the tools. If you want AI to actually design in Figma, use NPX Setup.
54
+ > **Bottom line:** Remote SSE is **read-only** with ~38% of the tools. **Cloud Mode** unlocks write access from web AI clients without Node.js. NPX/Local Git gives the full 57+ tools with real-time monitoring.
51
55
 
52
56
  ---
53
57
 
@@ -227,7 +231,53 @@ claude mcp add figma-console -s user -- npx -y mcp-remote@latest https://figma-c
227
231
 
228
232
  #### Upgrading to Full Capabilities
229
233
 
230
- Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide above.
234
+ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide above, or try [Cloud Mode](#-cloud-mode-web-ai-clients) if you don't want to install Node.js.
235
+
236
+ **πŸ“– [Complete Setup Guide](docs/setup.md)**
237
+
238
+ ---
239
+
240
+ ### ☁️ Cloud Mode (Web AI Clients)
241
+
242
+ **Best for:** Using Claude.ai, v0, Replit, or Lovable to create and modify Figma designs β€” no Node.js required.
243
+
244
+ **What you get:** 43 tools including full write access β€” design creation, variable management, component instantiation, and all REST API tools. Only real-time monitoring (console logs, selection tracking, document changes) requires Local Mode.
245
+
246
+ #### Prerequisites
247
+
248
+ - [ ] **Figma Desktop** with the Desktop Bridge plugin installed (see [Desktop Bridge setup](#step-3-connect-to-figma-desktop))
249
+ - [ ] **A web AI client** connected to the remote MCP endpoint (see [Remote SSE setup](#-remote-sse-read-only-exploration))
250
+
251
+ #### How to Connect
252
+
253
+ 1. **Set up Remote SSE** if you haven't already β€” follow the [Remote SSE](#-remote-sse-read-only-exploration) steps above
254
+ 2. **Open the Desktop Bridge plugin** in Figma Desktop (Plugins β†’ Development β†’ Figma Desktop Bridge)
255
+ 3. **Tell your AI assistant:**
256
+ ```
257
+ Connect to my Figma plugin
258
+ ```
259
+ 4. **The AI gives you a 6-character pairing code** (expires in 5 minutes)
260
+ 5. **In the plugin:** Toggle "Cloud Mode" β†’ enter the code β†’ click Connect
261
+ 6. **You're paired!** Full write access is now available
262
+
263
+ #### What You Can Do
264
+
265
+ Once paired, use natural language to design:
266
+ ```
267
+ Create a card component with a header image, title, description, and action button
268
+ Set up a color token collection with Light and Dark modes
269
+ Add a "High Contrast" mode to my existing token collection
270
+ ```
271
+
272
+ #### How It Works
273
+
274
+ Your AI client sends write commands through the cloud MCP server, which relays them via WebSocket to the Desktop Bridge plugin running in your Figma Desktop. The plugin executes the commands using the Figma Plugin API and returns results back through the same path.
275
+
276
+ ```
277
+ AI Client β†’ Cloud MCP Server β†’ Durable Object Relay β†’ Desktop Bridge Plugin β†’ Figma
278
+ ```
279
+
280
+ > **Variables on any plan:** Cloud Mode uses the Plugin API (not the Enterprise REST API), so variable management works on Free, Pro, and Organization plans.
231
281
 
232
282
  **πŸ“– [Complete Setup Guide](docs/setup.md)**
233
283
 
@@ -235,22 +285,24 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
235
285
 
236
286
  ## πŸ“Š Installation Method Comparison
237
287
 
238
- | Feature | NPX (Recommended) | Local Git | Remote SSE |
239
- |---------|-------------------|-----------|------------|
240
- | **Setup time** | ~10 minutes | ~15 minutes | ~2 minutes |
241
- | **Total tools** | **56+** | **56+** | **22** (read-only) |
242
- | **Design creation** | βœ… | βœ… | ❌ |
243
- | **Variable management** | βœ… | βœ… | ❌ |
244
- | **Component instantiation** | βœ… | βœ… | ❌ |
245
- | **Desktop Bridge plugin** | βœ… | βœ… | ❌ |
246
- | **Variables (no Enterprise)** | βœ… | βœ… | ❌ |
247
- | **Console logs** | βœ… (zero latency) | βœ… (zero latency) | βœ… |
248
- | **Read design data** | βœ… | βœ… | βœ… |
249
- | **Authentication** | PAT (manual) | PAT (manual) | OAuth (automatic) |
250
- | **Automatic updates** | βœ… (`@latest`) | Manual (`git pull`) | βœ… |
251
- | **Source code access** | ❌ | βœ… | ❌ |
252
-
253
- > **Key insight:** Remote SSE is read-only with ~34% of the tools. Use NPX for full capabilities.
288
+ | Feature | NPX (Recommended) | Cloud Mode | Local Git | Remote SSE |
289
+ |---------|-------------------|------------|-----------|------------|
290
+ | **Setup time** | ~10 minutes | ~5 minutes | ~15 minutes | ~2 minutes |
291
+ | **Total tools** | **57+** | **43** | **57+** | **22** (read-only) |
292
+ | **Design creation** | βœ… | βœ… | βœ… | ❌ |
293
+ | **Variable management** | βœ… | βœ… | βœ… | ❌ |
294
+ | **Component instantiation** | βœ… | βœ… | βœ… | ❌ |
295
+ | **Real-time monitoring** | βœ… | ❌ | βœ… | ❌ |
296
+ | **Desktop Bridge plugin** | βœ… | βœ… | βœ… | ❌ |
297
+ | **Variables (no Enterprise)** | βœ… | βœ… | βœ… | ❌ |
298
+ | **Console logs** | βœ… (zero latency) | ❌ | βœ… (zero latency) | βœ… |
299
+ | **Read design data** | βœ… | βœ… | βœ… | βœ… |
300
+ | **Requires Node.js** | Yes | **No** | Yes | No |
301
+ | **Authentication** | PAT (manual) | OAuth (automatic) | PAT (manual) | OAuth (automatic) |
302
+ | **Automatic updates** | βœ… (`@latest`) | βœ… | Manual (`git pull`) | βœ… |
303
+ | **Source code access** | ❌ | ❌ | βœ… | ❌ |
304
+
305
+ > **Key insight:** Remote SSE is read-only. Cloud Mode adds write access for web AI clients without Node.js. NPX/Local Git give the full 57+ tools.
254
306
 
255
307
  **πŸ“– [Complete Feature Comparison](docs/mode-comparison.md)**
256
308
 
@@ -260,7 +312,7 @@ Ready for design creation? Follow the [NPX Setup](#-npx-setup-recommended) guide
260
312
 
261
313
  After setup, try these prompts:
262
314
 
263
- **Basic test (both modes):**
315
+ **Basic test (all modes):**
264
316
  ```
265
317
  Navigate to https://www.figma.com and check status
266
318
  ```
@@ -270,6 +322,12 @@ Navigate to https://www.figma.com and check status
270
322
  Get design variables from [your Figma file URL]
271
323
  ```
272
324
 
325
+ **Cloud Mode test:**
326
+ ```
327
+ Connect to my Figma plugin
328
+ ```
329
+ β†’ Follow the pairing flow, then try: "Create a simple blue rectangle"
330
+
273
331
  **Plugin test (Local Mode only):**
274
332
  ```
275
333
  Show me the primary font for [your theme name]
@@ -320,7 +378,10 @@ When you first use design system tools:
320
378
  - `figma_get_file_data` - Full file structure
321
379
  - `figma_get_file_for_plugin` - Optimized file data
322
380
 
323
- ### ✏️ Design Creation (Local Mode + Desktop Bridge)
381
+ ### ☁️ Cloud Relay
382
+ - `figma_pair_plugin` - Generate a pairing code to connect a Desktop Bridge plugin via the cloud relay
383
+
384
+ ### ✏️ Design Creation (Local Mode + Cloud Mode)
324
385
  - `figma_execute` - **Power tool**: Run any Figma Plugin API code to create designs
325
386
  - Create frames, shapes, text, components
326
387
  - Apply auto-layout, styles, effects
@@ -341,7 +402,7 @@ When you first use design system tools:
341
402
  - `figma_check_design_parity` - Compare Figma component specs against code implementation, producing a scored diff report with actionable fix items
342
403
  - `figma_generate_component_doc` - Generate platform-agnostic markdown documentation by merging Figma design data with code-side info
343
404
 
344
- ### πŸ”§ Variable Management (Local Mode + Desktop Bridge)
405
+ ### πŸ”§ Variable Management (Local Mode + Cloud Mode)
345
406
  - `figma_create_variable_collection` - Create new variable collections with modes
346
407
  - `figma_create_variable` - Create COLOR, FLOAT, STRING, or BOOLEAN variables
347
408
  - `figma_update_variable` - Update variable values in specific modes
@@ -360,6 +421,13 @@ When you first use design system tools:
360
421
 
361
422
  ## πŸ“– Example Prompts
362
423
 
424
+ ### Cloud Mode (Web AI Clients)
425
+ ```
426
+ Connect to my Figma plugin so we can start designing
427
+ Pair with my Figma file and create a login form with email, password, and submit button
428
+ Set up a brand color token collection with Light and Dark modes
429
+ ```
430
+
363
431
  ### Plugin Debugging
364
432
  ```
365
433
  Navigate to my Figma plugin and show me any console errors
@@ -375,7 +443,7 @@ Get the Button component with a visual reference image
375
443
  Get the Badge component in reconstruction format for programmatic creation
376
444
  ```
377
445
 
378
- ### Design Creation (Local Mode)
446
+ ### Design Creation (Local Mode + Cloud Mode)
379
447
  ```
380
448
  Create a success notification card with a checkmark icon and message
381
449
  Design a button component with hover and disabled states
@@ -385,7 +453,7 @@ Arrange these button variants into a component set
385
453
  Organize my icon variants as a proper component set with the purple border
386
454
  ```
387
455
 
388
- ### Variable Management (Local Mode)
456
+ ### Variable Management (Local Mode + Cloud Mode)
389
457
  ```
390
458
  Create a new color collection called "Brand Colors" with Light and Dark modes
391
459
  Add a primary color variable with value #3B82F6 for Light and #60A5FA for Dark
@@ -412,7 +480,7 @@ Navigate to this file and capture what's on screen
412
480
 
413
481
  ## 🎨 AI-Assisted Design Creation
414
482
 
415
- > **⚠️ Local Mode Only:** This feature requires the Desktop Bridge plugin and only works with Local Mode installation (NPX or Local Git). Remote Mode is read-only and cannot create or modify designs.
483
+ > **Requires Desktop Bridge:** This feature works with Local Mode (NPX or Local Git) and [Cloud Mode](#-cloud-mode-web-ai-clients). Remote SSE without Cloud Mode pairing is read-only and cannot create or modify designs.
416
484
 
417
485
  One of the most powerful capabilities of this MCP server is the ability to **design complete UI components and pages directly in Figma through natural language conversation** with any MCP-compatible AI assistant like Claude Desktop or Claude Code.
418
486
 
@@ -527,7 +595,9 @@ The **Figma Desktop Bridge** plugin is the recommended way to connect Figma to t
527
595
  - `FIGMA_WS_PORT` β€” Override the preferred WebSocket port (default: 9223). The server will fall back through a 10-port range starting from this value if the preferred port is occupied.
528
596
  - `FIGMA_WS_HOST` β€” Override the WebSocket server bind address (default: `localhost`). Set to `0.0.0.0` when running inside Docker so the host machine can reach the MCP server.
529
597
 
530
- **Plugin Limitation:** Only works in Local Mode (NPX or Local Git). Remote SSE mode cannot access it.
598
+ **Cloud Mode:** The plugin also supports a **Cloud Mode** toggle for pairing with web AI clients (Claude.ai, v0, Replit, Lovable). Toggle "Cloud Mode" in the plugin UI, enter the 6-character pairing code from your AI assistant, and click Connect. See [Cloud Mode](#-cloud-mode-web-ai-clients) for details.
599
+
600
+ **Plugin Limitation:** In Local Mode, works with NPX or Local Git. In Cloud Mode, pairs with the remote MCP endpoint. Remote SSE without Cloud Mode pairing is read-only.
531
601
 
532
602
  ---
533
603
 
@@ -653,9 +723,10 @@ The architecture supports adding new apps with minimal boilerplate β€” each app
653
723
 
654
724
  ## πŸ›€οΈ Roadmap
655
725
 
656
- **Current Status:** v1.11.2 (Stable) - Production-ready with Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 57+ tools, Comments API, and MCP Apps
726
+ **Current Status:** v1.12.0 (Stable) - Production-ready with Cloud Write Relay, Design System Kit, WebSocket-only connectivity, smart multi-file tracking, 57+ tools, Comments API, and MCP Apps
657
727
 
658
728
  **Recent Releases:**
729
+ - [x] **v1.12.0** - Cloud Write Relay: web AI clients (Claude.ai, v0, Replit, Lovable) can create and modify Figma designs via cloud relay pairing β€” no Node.js required
659
730
  - [x] **v1.11.2** - Screenshot fix: `figma_take_screenshot` works without explicit `nodeId` in WebSocket mode
660
731
  - [x] **v1.11.1** - Doc generator fixes: clean markdown tables, Storybook links, property metadata filtering
661
732
  - [x] **v1.11.0** - Complete CDP removal, improved multi-file active tracking with focus detection
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Cloud WebSocket Connector
3
+ *
4
+ * Implements IFigmaConnector by routing commands through the PluginRelayDO
5
+ * Durable Object. Each method maps to a command sent via fetch() RPC to
6
+ * the relay, which forwards it to the Figma Desktop Bridge plugin over
7
+ * WebSocket.
8
+ *
9
+ * Structurally mirrors WebSocketConnector β€” same methods, different transport.
10
+ */
11
+ export class CloudWebSocketConnector {
12
+ constructor(relayStub) {
13
+ this.relayStub = relayStub;
14
+ }
15
+ async initialize() {
16
+ const res = await this.relayStub.fetch('https://relay/relay/status');
17
+ const status = await res.json();
18
+ if (!status.connected) {
19
+ throw new Error('No plugin connected to cloud relay. User must pair the Desktop Bridge plugin first (use figma_pair_plugin tool).');
20
+ }
21
+ }
22
+ getTransportType() {
23
+ return 'websocket';
24
+ }
25
+ // ============================================================================
26
+ // Core execution
27
+ // ============================================================================
28
+ async executeInPluginContext(code) {
29
+ return this.sendCommand('EXECUTE_CODE', { code, timeout: 5000 }, 7000);
30
+ }
31
+ async getVariablesFromPluginUI(fileKey) {
32
+ return this.sendCommand('GET_VARIABLES_DATA', {}, 10000);
33
+ }
34
+ async getVariables(fileKey) {
35
+ const code = `
36
+ (async () => {
37
+ try {
38
+ if (typeof figma === 'undefined') {
39
+ throw new Error('Figma API not available in this context');
40
+ }
41
+ const variables = await figma.variables.getLocalVariablesAsync();
42
+ const collections = await figma.variables.getLocalVariableCollectionsAsync();
43
+ return {
44
+ success: true,
45
+ timestamp: Date.now(),
46
+ fileMetadata: { fileName: figma.root.name, fileKey: figma.fileKey || null },
47
+ variables: variables.map(function(v) { return { id: v.id, name: v.name, key: v.key, resolvedType: v.resolvedType, valuesByMode: v.valuesByMode, variableCollectionId: v.variableCollectionId, scopes: v.scopes, description: v.description, hiddenFromPublishing: v.hiddenFromPublishing }; }),
48
+ variableCollections: collections.map(function(c) { return { id: c.id, name: c.name, key: c.key, modes: c.modes, defaultModeId: c.defaultModeId, variableIds: c.variableIds }; })
49
+ };
50
+ } catch (error) {
51
+ return { success: false, error: error.message };
52
+ }
53
+ })()
54
+ `;
55
+ return this.sendCommand('EXECUTE_CODE', { code, timeout: 30000 }, 32000);
56
+ }
57
+ async executeCodeViaUI(code, timeoutMs = 5000) {
58
+ return this.sendCommand('EXECUTE_CODE', { code, timeout: timeoutMs }, timeoutMs + 2000);
59
+ }
60
+ // ============================================================================
61
+ // Variable operations
62
+ // ============================================================================
63
+ async updateVariable(variableId, modeId, value) {
64
+ return this.sendCommand('UPDATE_VARIABLE', { variableId, modeId, value });
65
+ }
66
+ async createVariable(name, collectionId, resolvedType, options) {
67
+ const params = { name, collectionId, resolvedType };
68
+ if (options) {
69
+ if (options.valuesByMode)
70
+ params.valuesByMode = options.valuesByMode;
71
+ if (options.description)
72
+ params.description = options.description;
73
+ if (options.scopes)
74
+ params.scopes = options.scopes;
75
+ }
76
+ return this.sendCommand('CREATE_VARIABLE', params);
77
+ }
78
+ async deleteVariable(variableId) {
79
+ return this.sendCommand('DELETE_VARIABLE', { variableId });
80
+ }
81
+ async refreshVariables() {
82
+ return this.sendCommand('REFRESH_VARIABLES', {}, 300000);
83
+ }
84
+ async renameVariable(variableId, newName) {
85
+ const result = await this.sendCommand('RENAME_VARIABLE', { variableId, newName });
86
+ if (!result.oldName && result.variable?.oldName)
87
+ result.oldName = result.variable.oldName;
88
+ return result;
89
+ }
90
+ async setVariableDescription(variableId, description) {
91
+ return this.sendCommand('SET_VARIABLE_DESCRIPTION', { variableId, description });
92
+ }
93
+ // ============================================================================
94
+ // Mode operations
95
+ // ============================================================================
96
+ async addMode(collectionId, modeName) {
97
+ return this.sendCommand('ADD_MODE', { collectionId, modeName });
98
+ }
99
+ async renameMode(collectionId, modeId, newName) {
100
+ const result = await this.sendCommand('RENAME_MODE', { collectionId, modeId, newName });
101
+ if (!result.oldName && result.collection?.oldName)
102
+ result.oldName = result.collection.oldName;
103
+ return result;
104
+ }
105
+ // ============================================================================
106
+ // Collection operations
107
+ // ============================================================================
108
+ async createVariableCollection(name, options) {
109
+ const params = { name };
110
+ if (options) {
111
+ if (options.initialModeName)
112
+ params.initialModeName = options.initialModeName;
113
+ if (options.additionalModes)
114
+ params.additionalModes = options.additionalModes;
115
+ }
116
+ return this.sendCommand('CREATE_VARIABLE_COLLECTION', params);
117
+ }
118
+ async deleteVariableCollection(collectionId) {
119
+ return this.sendCommand('DELETE_VARIABLE_COLLECTION', { collectionId });
120
+ }
121
+ // ============================================================================
122
+ // Component operations
123
+ // ============================================================================
124
+ async getComponentFromPluginUI(nodeId) {
125
+ return this.sendCommand('GET_COMPONENT', { nodeId }, 10000);
126
+ }
127
+ async getLocalComponents() {
128
+ return this.sendCommand('GET_LOCAL_COMPONENTS', {}, 300000);
129
+ }
130
+ async setNodeDescription(nodeId, description, descriptionMarkdown) {
131
+ return this.sendCommand('SET_NODE_DESCRIPTION', { nodeId, description, descriptionMarkdown });
132
+ }
133
+ async addComponentProperty(nodeId, propertyName, type, defaultValue, options) {
134
+ const params = { nodeId, propertyName, propertyType: type, defaultValue };
135
+ if (options?.preferredValues)
136
+ params.preferredValues = options.preferredValues;
137
+ return this.sendCommand('ADD_COMPONENT_PROPERTY', params);
138
+ }
139
+ async editComponentProperty(nodeId, propertyName, newValue) {
140
+ return this.sendCommand('EDIT_COMPONENT_PROPERTY', { nodeId, propertyName, newValue });
141
+ }
142
+ async deleteComponentProperty(nodeId, propertyName) {
143
+ return this.sendCommand('DELETE_COMPONENT_PROPERTY', { nodeId, propertyName });
144
+ }
145
+ async instantiateComponent(componentKey, options) {
146
+ const params = { componentKey };
147
+ if (options) {
148
+ if (options.nodeId)
149
+ params.nodeId = options.nodeId;
150
+ if (options.position)
151
+ params.position = options.position;
152
+ if (options.size)
153
+ params.size = options.size;
154
+ if (options.overrides)
155
+ params.overrides = options.overrides;
156
+ if (options.variant)
157
+ params.variant = options.variant;
158
+ if (options.parentId)
159
+ params.parentId = options.parentId;
160
+ }
161
+ return this.sendCommand('INSTANTIATE_COMPONENT', params);
162
+ }
163
+ // ============================================================================
164
+ // Node manipulation
165
+ // ============================================================================
166
+ async resizeNode(nodeId, width, height, withConstraints = true) {
167
+ return this.sendCommand('RESIZE_NODE', { nodeId, width, height, withConstraints });
168
+ }
169
+ async moveNode(nodeId, x, y) {
170
+ return this.sendCommand('MOVE_NODE', { nodeId, x, y });
171
+ }
172
+ async setNodeFills(nodeId, fills) {
173
+ return this.sendCommand('SET_NODE_FILLS', { nodeId, fills });
174
+ }
175
+ async setNodeStrokes(nodeId, strokes, strokeWeight) {
176
+ const params = { nodeId, strokes };
177
+ if (strokeWeight !== undefined)
178
+ params.strokeWeight = strokeWeight;
179
+ return this.sendCommand('SET_NODE_STROKES', params);
180
+ }
181
+ async setNodeOpacity(nodeId, opacity) {
182
+ return this.sendCommand('SET_NODE_OPACITY', { nodeId, opacity });
183
+ }
184
+ async setNodeCornerRadius(nodeId, radius) {
185
+ return this.sendCommand('SET_NODE_CORNER_RADIUS', { nodeId, radius });
186
+ }
187
+ async cloneNode(nodeId) {
188
+ return this.sendCommand('CLONE_NODE', { nodeId });
189
+ }
190
+ async deleteNode(nodeId) {
191
+ return this.sendCommand('DELETE_NODE', { nodeId });
192
+ }
193
+ async renameNode(nodeId, newName) {
194
+ return this.sendCommand('RENAME_NODE', { nodeId, newName });
195
+ }
196
+ async setTextContent(nodeId, characters, options) {
197
+ const params = { nodeId, text: characters };
198
+ if (options) {
199
+ if (options.fontSize)
200
+ params.fontSize = options.fontSize;
201
+ if (options.fontWeight)
202
+ params.fontWeight = options.fontWeight;
203
+ if (options.fontFamily)
204
+ params.fontFamily = options.fontFamily;
205
+ }
206
+ return this.sendCommand('SET_TEXT_CONTENT', params);
207
+ }
208
+ async createChildNode(parentId, nodeType, properties) {
209
+ return this.sendCommand('CREATE_CHILD_NODE', { parentId, nodeType, properties: properties || {} });
210
+ }
211
+ // ============================================================================
212
+ // Screenshot & instance properties
213
+ // ============================================================================
214
+ async captureScreenshot(nodeId, options) {
215
+ const params = { nodeId };
216
+ if (options?.format)
217
+ params.format = options.format;
218
+ if (options?.scale)
219
+ params.scale = options.scale;
220
+ return this.sendCommand('CAPTURE_SCREENSHOT', params, 30000);
221
+ }
222
+ async setInstanceProperties(nodeId, properties) {
223
+ return this.sendCommand('SET_INSTANCE_PROPERTIES', { nodeId, properties });
224
+ }
225
+ // ============================================================================
226
+ // Cache management (no-op for cloud relay)
227
+ // ============================================================================
228
+ clearFrameCache() {
229
+ // No frame cache in cloud relay mode
230
+ }
231
+ // ============================================================================
232
+ // Transport β€” fetch-based RPC to the relay DO
233
+ // ============================================================================
234
+ async sendCommand(method, params = {}, timeoutMs = 15000) {
235
+ const res = await this.relayStub.fetch('https://relay/relay/command', {
236
+ method: 'POST',
237
+ headers: { 'Content-Type': 'application/json' },
238
+ body: JSON.stringify({ method, params, timeoutMs }),
239
+ });
240
+ const data = await res.json();
241
+ if (data.error) {
242
+ throw new Error(data.error);
243
+ }
244
+ return data.result;
245
+ }
246
+ }