@dannote/figma-use 0.2.1 → 0.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/CHANGELOG.md +83 -1
- package/README.md +128 -10
- package/SKILL.md +51 -0
- package/dist/.figma-render-1768690678858.tsx +3 -0
- package/dist/.figma-render-1768690686527.tsx +3 -0
- package/dist/cli/index.js +25969 -2290
- package/dist/proxy/index.js +7531 -7
- package/package.json +4 -2
- package/packages/plugin/dist/main.js +338 -22
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-01-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`defineVars` API for Figma variables** — bind colors to variables by name
|
|
15
|
+
```tsx
|
|
16
|
+
const colors = defineVars({
|
|
17
|
+
primary: { name: 'Colors/Gray/50', value: '#F8FAFC' },
|
|
18
|
+
accent: { name: 'Colors/Blue/500', value: '#3B82F6' },
|
|
19
|
+
})
|
|
20
|
+
<Frame style={{ backgroundColor: colors.primary }} />
|
|
21
|
+
```
|
|
22
|
+
- Variable binding for `backgroundColor`, `borderColor`, and text `color`
|
|
23
|
+
- Variables resolved by name at render time (no more magic IDs)
|
|
24
|
+
- `defineVars` support in stdin snippets
|
|
25
|
+
- Explicit fallback values in `defineVars` for proper color display
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Auto-layout now works correctly via `trigger-layout` post-render
|
|
30
|
+
- Nested auto-layout frames trigger recursively
|
|
31
|
+
- Variable binding encoding matches Figma's exact wire format
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Marked React render and variable bindings as **experimental** in docs
|
|
36
|
+
|
|
37
|
+
## [0.3.1] - 2026-01-18
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **Variable binding via multiplayer protocol** — bind fill colors to Figma variables without plugin API
|
|
42
|
+
- `encodePaintWithVariableBinding()` — encode Paint with color variable binding
|
|
43
|
+
- `encodeNodeChangeWithVariables()` — encode NodeChange with variable-bound paints
|
|
44
|
+
- `parseVariableId()` — parse "VariableID:sessionID:localID" strings
|
|
45
|
+
- New exports: `VariableBinding`, `encodePaintWithVariableBinding`, `encodeNodeChangeWithVariables`, `parseVariableId`
|
|
46
|
+
- `bind-fill-variable` plugin command — bind fill color to variable
|
|
47
|
+
- `bind-stroke-variable` plugin command — bind stroke color to variable
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Message field mapping: nodeChanges is field 4, reconnectSequenceNumber is field 25
|
|
52
|
+
- Paint variable binding format now matches Figma's exact wire format
|
|
53
|
+
|
|
54
|
+
### Technical
|
|
55
|
+
|
|
56
|
+
- Discovered Figma's variable binding wire format via WebSocket traffic analysis
|
|
57
|
+
- Created capture/diff tools for binary protocol analysis (`scripts/capture.ts`, `scripts/diff-hex.ts`)
|
|
58
|
+
- 142 tests passing
|
|
59
|
+
|
|
60
|
+
## [0.3.0] - 2025-01-17
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **`render` command** — render React/TSX components directly to Figma
|
|
65
|
+
- From file: `figma-use render ./Card.figma.tsx`
|
|
66
|
+
- From stdin: `echo '<Frame style={{...}} />' | figma-use render --stdin`
|
|
67
|
+
- With props: `--props '{"title": "Hello"}'`
|
|
68
|
+
- Into parent: `--parent "1:23"`
|
|
69
|
+
- Dry run: `--dryRun` outputs NodeChanges JSON
|
|
70
|
+
- **Multiplayer WebSocket connection pooling** in proxy
|
|
71
|
+
- First render: ~4s (establishes connection)
|
|
72
|
+
- Subsequent renders: ~0.4s (10x faster!)
|
|
73
|
+
- Connections auto-close after 5min idle
|
|
74
|
+
- **React components** — `Frame`, `Text`, `Rectangle`, `Ellipse`, `Line`, `Star`, `Polygon`, `Vector`, `Component`, `Instance`, `Group`, `Page`, `View`
|
|
75
|
+
- **JSX intrinsic elements** — PascalCase in JSX, lowercase in output
|
|
76
|
+
- **culori integration** — robust color parsing (hex, rgb(), hsl(), named colors)
|
|
77
|
+
- `/render` endpoint in proxy for direct NodeChanges submission
|
|
78
|
+
- `/status` endpoint now shows multiplayer connection pool
|
|
79
|
+
|
|
80
|
+
### Changed
|
|
81
|
+
|
|
82
|
+
- Proxy now holds persistent WebSocket connections to Figma multiplayer
|
|
83
|
+
- Architecture diagram updated to show dual communication paths
|
|
84
|
+
- 143 tests passing
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
|
|
88
|
+
- TypeScript strict mode errors in tests
|
|
89
|
+
- NodeChanges validation before sending (must have guid)
|
|
90
|
+
|
|
10
91
|
## [0.2.1] - 2025-01-17
|
|
11
92
|
|
|
12
93
|
### Added
|
|
@@ -137,7 +218,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
137
218
|
- Export commands: PNG/SVG/PDF export, screenshot
|
|
138
219
|
- Inline styling: `--fill`, `--stroke`, `--radius` etc. on create commands
|
|
139
220
|
|
|
140
|
-
[unreleased]: https://github.com/dannote/figma-use/compare/v0.
|
|
221
|
+
[unreleased]: https://github.com/dannote/figma-use/compare/v0.3.0...HEAD
|
|
222
|
+
[0.3.0]: https://github.com/dannote/figma-use/compare/v0.2.1...v0.3.0
|
|
141
223
|
[0.2.1]: https://github.com/dannote/figma-use/compare/v0.2.0...v0.2.1
|
|
142
224
|
[0.2.0]: https://github.com/dannote/figma-use/compare/v0.1.5...v0.2.0
|
|
143
225
|
[0.1.5]: https://github.com/dannote/figma-use/compare/v0.1.4...v0.1.5
|
package/README.md
CHANGED
|
@@ -27,16 +27,28 @@ figma-use gives AI agents **full read/write control** over Figma.
|
|
|
27
27
|
## How it works
|
|
28
28
|
|
|
29
29
|
```
|
|
30
|
-
┌─────────────────┐
|
|
31
|
-
│ │
|
|
32
|
-
│ AI Agent /
|
|
33
|
-
│ CLI │ HTTP│ proxy │
|
|
34
|
-
│
|
|
35
|
-
│ │
|
|
36
|
-
└─────────────────┘
|
|
30
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
31
|
+
│ │ │ │ │ │
|
|
32
|
+
│ AI Agent / │─────▶│ figma-use │─────▶│ Figma │
|
|
33
|
+
│ CLI │ HTTP │ proxy │ WS │ Plugin │
|
|
34
|
+
│ │◀─────│ :38451 │◀─────│ │
|
|
35
|
+
│ │ │ │ │ │
|
|
36
|
+
└─────────────────┘ └────────┬────────┘ └─────────────────┘
|
|
37
|
+
│
|
|
38
|
+
│ WebSocket (persistent)
|
|
39
|
+
▼
|
|
40
|
+
┌─────────────────┐
|
|
41
|
+
│ Figma │
|
|
42
|
+
│ Multiplayer │
|
|
43
|
+
│ Server │
|
|
44
|
+
└─────────────────┘
|
|
37
45
|
```
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
Two communication paths:
|
|
48
|
+
- **Plugin API** — most commands go through the Figma plugin for full API access
|
|
49
|
+
- **Multiplayer WebSocket** — the `render` command writes directly to Figma's multiplayer server for ~100x faster node creation
|
|
50
|
+
|
|
51
|
+
The proxy maintains persistent connections for fast repeated operations.
|
|
40
52
|
|
|
41
53
|
## Installation
|
|
42
54
|
|
|
@@ -226,10 +238,10 @@ figma-use viewport zoom-to-fit <ids...>
|
|
|
226
238
|
```bash
|
|
227
239
|
figma-use find --name "Button"
|
|
228
240
|
figma-use find --name "Icon" --type FRAME
|
|
229
|
-
figma-use find --type INSTANCE
|
|
241
|
+
figma-use find --type INSTANCE --limit 50 # Limit results (default: 100)
|
|
230
242
|
figma-use get pages
|
|
231
243
|
figma-use get components --name "Button" # Filter by name
|
|
232
|
-
figma-use get components --limit 50 # Limit results (default:
|
|
244
|
+
figma-use get components --limit 50 # Limit results (default: 50)
|
|
233
245
|
figma-use get styles
|
|
234
246
|
```
|
|
235
247
|
|
|
@@ -246,6 +258,82 @@ figma-use group ungroup <id>
|
|
|
246
258
|
figma-use group flatten "1:2,1:3"
|
|
247
259
|
```
|
|
248
260
|
|
|
261
|
+
### Render React Components (Experimental)
|
|
262
|
+
|
|
263
|
+
> ⚠️ **Experimental**: The React render feature uses Figma's internal multiplayer protocol, which is undocumented and may change without notice. Use for prototyping and automation, not production workflows.
|
|
264
|
+
|
|
265
|
+
Render TSX/JSX components directly to Figma via WebSocket (bypasses plugin API for ~100x speed):
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# From file
|
|
269
|
+
figma-use render ./Card.figma.tsx
|
|
270
|
+
|
|
271
|
+
# With props
|
|
272
|
+
figma-use render ./Card.figma.tsx --props '{"title": "Hello", "items": ["A", "B"]}'
|
|
273
|
+
|
|
274
|
+
# JSX snippet from stdin
|
|
275
|
+
echo '<Frame style={{width: 200, height: 100, backgroundColor: "#FF0000"}} />' | figma-use render --stdin
|
|
276
|
+
|
|
277
|
+
# Nested elements
|
|
278
|
+
echo '<Frame style={{padding: 20, gap: 10}}>
|
|
279
|
+
<Text style={{fontSize: 24}}>Title</Text>
|
|
280
|
+
<Rectangle style={{width: 100, height: 50, backgroundColor: "#3B82F6"}} />
|
|
281
|
+
</Frame>' | figma-use render --stdin
|
|
282
|
+
|
|
283
|
+
# Full component from stdin (with imports/exports)
|
|
284
|
+
cat component.tsx | figma-use render --stdin
|
|
285
|
+
|
|
286
|
+
# Into specific parent
|
|
287
|
+
figma-use render ./Card.figma.tsx --parent "1:23"
|
|
288
|
+
|
|
289
|
+
# Dry run (output NodeChanges JSON without sending)
|
|
290
|
+
figma-use render ./Card.figma.tsx --dryRun
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Important:** The `render` command requires:
|
|
294
|
+
1. Figma running with remote debugging: `figma --remote-debugging-port=9222`
|
|
295
|
+
2. Proxy server running: `figma-use proxy`
|
|
296
|
+
|
|
297
|
+
The proxy maintains persistent WebSocket connections for fast repeated renders:
|
|
298
|
+
- First render: ~4s (establishes connection)
|
|
299
|
+
- Subsequent renders: ~0.4s (reuses connection)
|
|
300
|
+
|
|
301
|
+
Example component (`Card.figma.tsx`):
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
import * as React from 'react'
|
|
305
|
+
import { Frame, Text, Rectangle } from '@dannote/figma-use/components'
|
|
306
|
+
|
|
307
|
+
interface CardProps {
|
|
308
|
+
title: string
|
|
309
|
+
items: string[]
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export default function Card({ title, items }: CardProps) {
|
|
313
|
+
return (
|
|
314
|
+
<Frame name="Card" style={{
|
|
315
|
+
width: 300,
|
|
316
|
+
flexDirection: 'column',
|
|
317
|
+
padding: 24,
|
|
318
|
+
gap: 16,
|
|
319
|
+
backgroundColor: '#FFFFFF',
|
|
320
|
+
borderRadius: 12,
|
|
321
|
+
}}>
|
|
322
|
+
<Text name="Title" style={{ fontSize: 24, fontWeight: 'bold', color: '#000' }}>
|
|
323
|
+
{title}
|
|
324
|
+
</Text>
|
|
325
|
+
<Frame name="Items" style={{ flexDirection: 'column', gap: 8 }}>
|
|
326
|
+
{items.map((item, i) => (
|
|
327
|
+
<Text key={i} style={{ fontSize: 16, color: '#666' }}>{item}</Text>
|
|
328
|
+
))}
|
|
329
|
+
</Frame>
|
|
330
|
+
</Frame>
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Available elements: `Frame`, `Rectangle`, `Ellipse`, `Text`, `Line`, `Star`, `Polygon`, `Vector`, `Component`, `Instance`, `Group`, `Page`, `View`
|
|
336
|
+
|
|
249
337
|
### Advanced
|
|
250
338
|
|
|
251
339
|
```bash
|
|
@@ -357,3 +445,33 @@ Workflow:
|
|
|
357
445
|
## License
|
|
358
446
|
|
|
359
447
|
MIT
|
|
448
|
+
|
|
449
|
+
### Variable Bindings (Experimental)
|
|
450
|
+
|
|
451
|
+
> ⚠️ **Experimental**: Variable binding uses reverse-engineered protocol. Supports `backgroundColor`, `borderColor`, and text `color`.
|
|
452
|
+
|
|
453
|
+
Bind Figma variables to colors using human-readable names:
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
// tokens.figma.ts
|
|
457
|
+
import { defineVars } from '@dannote/figma-use'
|
|
458
|
+
|
|
459
|
+
export const colors = defineVars({
|
|
460
|
+
primary: { name: 'Colors/Gray/50', value: '#F8FAFC' },
|
|
461
|
+
accent: { name: 'Colors/Blue/500', value: '#3B82F6' },
|
|
462
|
+
text: { name: 'Colors/Gray/900', value: '#0F172A' },
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
// Card.figma.tsx
|
|
466
|
+
import { colors } from './tokens.figma'
|
|
467
|
+
|
|
468
|
+
export function Card({ title }: { title: string }) {
|
|
469
|
+
return (
|
|
470
|
+
<Frame style={{ backgroundColor: colors.primary }}>
|
|
471
|
+
<Text style={{ color: colors.text }}>{title}</Text>
|
|
472
|
+
</Frame>
|
|
473
|
+
)
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
The `value` field provides a fallback color for display. Variables are bound at the protocol level — no plugin API calls needed.
|
package/SKILL.md
CHANGED
|
@@ -148,6 +148,57 @@ figma-use group create "1:2,1:3"
|
|
|
148
148
|
figma-use group ungroup <id>
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
+
### Render React Components (Experimental)
|
|
152
|
+
|
|
153
|
+
> ⚠️ Uses Figma's internal multiplayer protocol — may break without notice.
|
|
154
|
+
|
|
155
|
+
Render TSX/JSX directly to Figma (~100x faster than plugin API):
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
# From file
|
|
159
|
+
figma-use render ./Card.figma.tsx
|
|
160
|
+
|
|
161
|
+
# JSX snippet from stdin
|
|
162
|
+
echo '<Frame style={{width: 200, height: 100, backgroundColor: "#FF0000"}} />' | figma-use render --stdin
|
|
163
|
+
|
|
164
|
+
# Nested elements
|
|
165
|
+
echo '<Frame style={{padding: 20, gap: 10, flexDirection: "column"}}>
|
|
166
|
+
<Text style={{fontSize: 24, color: "#000"}}>Title</Text>
|
|
167
|
+
<Rectangle style={{width: 100, height: 50, backgroundColor: "#3B82F6"}} />
|
|
168
|
+
</Frame>' | figma-use render --stdin
|
|
169
|
+
|
|
170
|
+
# With props
|
|
171
|
+
figma-use render ./Card.figma.tsx --props '{"title": "Hello"}'
|
|
172
|
+
|
|
173
|
+
# Into specific parent
|
|
174
|
+
figma-use render ./Card.figma.tsx --parent "1:23"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Requires:**
|
|
178
|
+
1. Figma with `figma --remote-debugging-port=9222`
|
|
179
|
+
2. Proxy running: `figma-use proxy`
|
|
180
|
+
|
|
181
|
+
Available elements: `Frame`, `Rectangle`, `Ellipse`, `Text`, `Line`, `Star`, `Polygon`, `Vector`, `Component`, `Instance`, `Group`
|
|
182
|
+
|
|
183
|
+
#### Variable Bindings (Experimental)
|
|
184
|
+
|
|
185
|
+
Bind Figma variables to colors by name with fallback values:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
import { defineVars, Frame } from '@dannote/figma-use'
|
|
189
|
+
|
|
190
|
+
const colors = defineVars({
|
|
191
|
+
primary: { name: 'Colors/Gray/50', value: '#F8FAFC' },
|
|
192
|
+
border: { name: 'Colors/Gray/500', value: '#64748B' },
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
export default () => (
|
|
196
|
+
<Frame style={{ backgroundColor: colors.primary, borderColor: colors.border }} />
|
|
197
|
+
)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Supports: `backgroundColor`, `borderColor`, text `color`.
|
|
201
|
+
|
|
151
202
|
### Eval (Arbitrary Code)
|
|
152
203
|
|
|
153
204
|
```bash
|