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.
- package/README.md +107 -36
- package/dist/cloudflare/core/cloud-websocket-connector.js +246 -0
- package/dist/cloudflare/core/cloud-websocket-relay.js +198 -0
- package/dist/cloudflare/core/comment-tools.js +3 -3
- package/dist/cloudflare/core/console-monitor.js +1 -1
- package/dist/cloudflare/core/design-code-tools.js +956 -91
- package/dist/cloudflare/core/figma-connector.js +2 -3
- package/dist/cloudflare/core/figma-desktop-connector.js +3 -2
- package/dist/cloudflare/core/figma-tools.js +22 -22
- package/dist/cloudflare/core/port-discovery.js +96 -5
- package/dist/cloudflare/core/websocket-connector.js +1 -1
- package/dist/cloudflare/core/websocket-server.js +35 -5
- package/dist/cloudflare/core/write-tools.js +2006 -0
- package/dist/cloudflare/index.js +174 -11
- package/dist/core/cloud-websocket-connector.d.ts +58 -0
- package/dist/core/cloud-websocket-connector.d.ts.map +1 -0
- package/dist/core/cloud-websocket-connector.js +247 -0
- package/dist/core/cloud-websocket-connector.js.map +1 -0
- package/dist/core/cloud-websocket-relay.d.ts +51 -0
- package/dist/core/cloud-websocket-relay.d.ts.map +1 -0
- package/dist/core/cloud-websocket-relay.js +171 -0
- package/dist/core/cloud-websocket-relay.js.map +1 -0
- package/dist/core/write-tools.d.ts +7 -0
- package/dist/core/write-tools.d.ts.map +1 -0
- package/dist/core/write-tools.js +2007 -0
- package/dist/core/write-tools.js.map +1 -0
- package/figma-desktop-bridge/README.md +58 -6
- package/figma-desktop-bridge/code.js +17 -2
- package/figma-desktop-bridge/manifest.json +7 -3
- package/figma-desktop-bridge/ui.html +236 -5
- 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
|
-
> **π
|
|
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
|
-
-
|
|
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
|
-
|
|
|
48
|
-
|
|
|
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 ~
|
|
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** | **
|
|
242
|
-
| **Design creation** | β
| β
| β |
|
|
243
|
-
| **Variable management** | β
| β
| β |
|
|
244
|
-
| **Component instantiation** | β
| β
| β |
|
|
245
|
-
| **
|
|
246
|
-
| **
|
|
247
|
-
| **
|
|
248
|
-
| **
|
|
249
|
-
| **
|
|
250
|
-
| **
|
|
251
|
-
| **
|
|
252
|
-
|
|
253
|
-
|
|
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 (
|
|
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
|
-
###
|
|
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 +
|
|
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
|
-
>
|
|
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
|
-
**
|
|
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.
|
|
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
|
+
}
|