@dannote/figma-use 0.5.0 → 0.5.2

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 CHANGED
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.2] - 2026-01-18
11
+
12
+ ### Fixed
13
+
14
+ - Multiplayer connection works with Figma's updated protocol (sessionID now from plugin API)
15
+ - Proxy properly handles file switches (closes stale connections)
16
+ - `figma-use status` now shows full connection diagnostics (proxy, plugin, DevTools, file)
17
+
18
+ ## [0.5.1] - 2026-01-18
19
+
20
+ ### Added
21
+
22
+ - Package exports for `@dannote/figma-use/render` and `@dannote/figma-use/components`
23
+
24
+ ### Fixed
25
+
26
+ - SKILL.md now starts with connection check instructions
27
+ - Simplified SKILL.md for better AI agent comprehension
28
+
10
29
  ## [0.5.0] - 2026-01-18
11
30
 
12
31
  ### Added
package/SKILL.md CHANGED
@@ -7,277 +7,156 @@ description: Control Figma via CLI — create shapes, frames, text, components,
7
7
 
8
8
  Full control over Figma from the command line.
9
9
 
10
- ## Setup
10
+ ## Before You Start
11
11
 
12
- ```bash
13
- bun install -g @dannote/figma-use
14
- figma-use plugin # Install plugin (quit Figma first!)
15
- figma-use proxy # Start proxy server
16
- # Open Figma → Plugins → Development → Figma Use
17
- ```
18
-
19
- ## Quick Reference
20
-
21
- ### Create Shapes
12
+ **Always check connection first:**
22
13
 
23
14
  ```bash
24
- figma-use create rect --x 0 --y 0 --width 100 --height 50 --fill "#FF0000" --radius 8
25
- figma-use create ellipse --x 0 --y 0 --width 80 --height 80 --fill "#00FF00"
26
- figma-use create line --x 0 --y 0 --length 100 --stroke "#000"
27
- figma-use create polygon --x 0 --y 0 --size 60 --sides 6 --fill "#F59E0B"
28
- figma-use create star --x 0 --y 0 --size 60 --points 5 --fill "#EF4444"
15
+ figma-use status
29
16
  ```
30
17
 
31
- ### Create Frames (with auto-layout)
32
-
18
+ If not connected:
33
19
  ```bash
34
- figma-use create frame --x 0 --y 0 --width 400 --height 300 \
35
- --fill "#FFF" --radius 12 --name "Card" \
36
- --layout VERTICAL --gap 16 --padding "24,24,24,24"
20
+ # 1. Start proxy (keep running in background)
21
+ figma-use proxy &
37
22
 
38
- figma-use create frame --x 0 --y 0 --width 200 --height 48 \
39
- --fill "#3B82F6" --radius 8 --name "Button" \
40
- --layout HORIZONTAL --gap 8 --padding "12,24,12,24"
41
- ```
23
+ # 2. Start Figma with debug port (for render command)
24
+ figma --remote-debugging-port=9222
42
25
 
43
- ### Create Text
44
-
45
- ```bash
46
- figma-use create text --x 0 --y 0 --text "Hello" \
47
- --fontSize 24 --fontFamily "Inter" --fontStyle "Bold" --fill "#000"
26
+ # 3. In Figma: Plugins → Development → Figma Use
48
27
  ```
49
28
 
50
- ### Node Operations
51
-
29
+ If plugin not installed:
52
30
  ```bash
53
- figma-use node get <id> # Get properties
54
- figma-use node tree [id] # Get formatted tree (see structure at a glance)
55
- figma-use node tree --depth 2 # Limit tree depth (also limits node count)
56
- figma-use node tree -i # Only interactive elements
57
- figma-use node tree --force # Skip 500 node limit
58
- figma-use node children <id> # List children
59
- figma-use node move <id> --x 100 --y 200
60
- figma-use node resize <id> --width 300 --height 200
61
- figma-use node rename <id> "New Name"
62
- figma-use node clone <id>
63
- figma-use node delete <id>
31
+ # Quit Figma first, then:
32
+ figma-use plugin
64
33
  ```
65
34
 
66
- `tree` output example:
67
- ```
68
- [0] frame "Card" (1:23)
69
- 400×300 at (0, 0) | fill: #FFFFFF | layout: col gap=16
70
- [0] text "Title" (1:24)
71
- 200×32 at (24, 24) | "Hello World" | font: 24px Inter
72
- ```
35
+ ---
36
+
37
+ ## JSX Rendering (Fastest Way)
73
38
 
74
- ### Set Properties
39
+ For complex layouts, use `render --stdin` with pure JSX. No imports needed — elements are built-in:
75
40
 
76
41
  ```bash
77
- figma-use set fill <id> "#FF0000"
78
- figma-use set stroke <id> "#000" --weight 2
79
- figma-use set radius <id> --radius 12
80
- figma-use set radius <id> --topLeft 16 --bottomRight 16
81
- figma-use set opacity <id> 0.8
82
- figma-use set rotation <id> 45
83
- figma-use set text <id> "Updated text"
84
- figma-use set font <id> --family "Inter" --style "Bold" --size 20
85
- figma-use set layout <id> --mode HORIZONTAL --gap 8 --padding 16
86
- figma-use set effect <id> --type DROP_SHADOW --radius 10 --offsetY 4 --color "#00000040"
42
+ echo '<Frame style={{padding: 24, gap: 16, flexDirection: "column", backgroundColor: "#FFF", borderRadius: 12}}>
43
+ <Text style={{fontSize: 24, fontWeight: "bold", color: "#000"}}>Card Title</Text>
44
+ <Text style={{fontSize: 14, color: "#666"}}>Description text here</Text>
45
+ </Frame>' | figma-use render --stdin
87
46
  ```
88
47
 
89
- ### Variables (Design Tokens)
48
+ **Elements:** `Frame`, `Rectangle`, `Ellipse`, `Text`, `Line`, `Star`, `Polygon`, `Vector`, `Group`
90
49
 
91
- ```bash
92
- figma-use collection list
93
- figma-use collection create "Colors"
50
+ **Style props:** `width`, `height`, `x`, `y`, `padding`, `paddingTop/Right/Bottom/Left`, `gap`, `flexDirection` (row|column), `justifyContent`, `alignItems`, `backgroundColor`, `borderColor`, `borderWidth`, `borderRadius`, `opacity`, `fontSize`, `fontFamily`, `fontWeight`, `color`, `textAlign`
94
51
 
95
- figma-use variable list --type COLOR
96
- figma-use variable create "Primary" --collection <collectionId> --type COLOR --value "#3B82F6"
97
- figma-use variable set <varId> --mode <modeId> --value "#1D4ED8"
98
- figma-use variable bind --node <nodeId> --field fills --variable <varId>
99
- ```
100
-
101
- ### Styles
52
+ ### Buttons Example (3 sizes)
102
53
 
103
54
  ```bash
104
- figma-use style list
105
- figma-use style create-paint "Brand/Primary" --color "#E11D48"
106
- figma-use style create-text "Heading/H1" --family "Inter" --style "Bold" --size 32
55
+ echo '<Frame style={{gap: 16, flexDirection: "row", padding: 24}}>
56
+ <Frame name="Small" style={{paddingLeft: 12, paddingRight: 12, paddingTop: 6, paddingBottom: 6, backgroundColor: "#3B82F6", borderRadius: 6, flexDirection: "row", justifyContent: "center", alignItems: "center"}}>
57
+ <Text style={{fontSize: 12, color: "#FFF"}}>Button</Text>
58
+ </Frame>
59
+ <Frame name="Medium" style={{paddingLeft: 16, paddingRight: 16, paddingTop: 8, paddingBottom: 8, backgroundColor: "#3B82F6", borderRadius: 6, flexDirection: "row", justifyContent: "center", alignItems: "center"}}>
60
+ <Text style={{fontSize: 14, color: "#FFF"}}>Button</Text>
61
+ </Frame>
62
+ <Frame name="Large" style={{paddingLeft: 24, paddingRight: 24, paddingTop: 12, paddingBottom: 12, backgroundColor: "#3B82F6", borderRadius: 6, flexDirection: "row", justifyContent: "center", alignItems: "center"}}>
63
+ <Text style={{fontSize: 16, color: "#FFF"}}>Button</Text>
64
+ </Frame>
65
+ </Frame>' | figma-use render --stdin
107
66
  ```
108
67
 
109
- ### Export
68
+ ### Advanced: Components & Variants
110
69
 
111
- ```bash
112
- figma-use export node <id> --format PNG --scale 2 --output design.png
113
- figma-use export screenshot --output viewport.png
114
- figma-use export selection --output selection.png
115
- ```
70
+ For `defineComponent`, `defineComponentSet`, `defineVars` — use files with imports:
116
71
 
117
- Heavy ops support `--timeout` (seconds):
118
72
  ```bash
119
- figma-use export node <id> --timeout 300
73
+ figma-use render --examples # Full API reference
74
+ figma-use render ./MyComponent.figma.tsx
120
75
  ```
121
76
 
122
- ### Selection & Navigation
77
+ ---
123
78
 
124
- ```bash
125
- figma-use selection get
126
- figma-use selection set "1:2,1:3"
127
- figma-use page list
128
- figma-use page set "Page Name"
129
- figma-use viewport zoom-to-fit <ids...>
130
- ```
79
+ ## CLI Commands
131
80
 
132
- ### Find
81
+ ### Create
133
82
 
134
83
  ```bash
135
- figma-use find --name "Button"
136
- figma-use find --type FRAME
137
- figma-use find --type INSTANCE # All instances on page
138
- figma-use get components --name "Button" # Filter components by name
139
- figma-use get components --limit 50 # Limit results
84
+ figma-use create frame --width 400 --height 300 --fill "#FFF" --radius 12 --layout VERTICAL --gap 16
85
+ figma-use create rect --width 100 --height 50 --fill "#FF0000" --radius 8
86
+ figma-use create ellipse --width 80 --height 80 --fill "#00FF00"
87
+ figma-use create text --text "Hello" --fontSize 24 --fill "#000"
88
+ figma-use create line --length 100 --stroke "#000"
140
89
  ```
141
90
 
142
- ### Boolean & Group
91
+ ### Query
143
92
 
144
93
  ```bash
145
- figma-use boolean union "1:2,1:3"
146
- figma-use boolean subtract "1:2,1:3"
147
- figma-use group create "1:2,1:3"
148
- figma-use group ungroup <id>
94
+ figma-use node get <id> # Node properties
95
+ figma-use node tree # Page structure
96
+ figma-use node tree --depth 2 # Limit depth
97
+ figma-use node children <id> # Children list
98
+ figma-use find --name "Button" # Find by name
99
+ figma-use find --type FRAME # Find by type
100
+ figma-use selection get # Current selection
149
101
  ```
150
102
 
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):
103
+ ### Modify
156
104
 
157
105
  ```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"
106
+ figma-use set fill <id> "#FF0000"
107
+ figma-use set stroke <id> "#000" --weight 2
108
+ figma-use set radius <id> 12
109
+ figma-use set opacity <id> 0.5
110
+ figma-use set text <id> "New text"
111
+ figma-use set font <id> --family "Inter" --style "Bold" --size 20
112
+ figma-use set layout <id> --mode VERTICAL --gap 12 --padding 16
113
+ figma-use node move <id> --x 100 --y 200
114
+ figma-use node resize <id> --width 300 --height 200
115
+ figma-use node delete <id>
175
116
  ```
176
117
 
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
- #### Reusable Components
184
-
185
- **defineComponent** — simple reusable component:
186
- ```tsx
187
- import { defineComponent, Frame, Text } from '@dannote/figma-use/render'
188
-
189
- const Button = defineComponent('Button',
190
- <Frame style={{ padding: 12, backgroundColor: '#3B82F6', borderRadius: 8 }}>
191
- <Text style={{ color: '#FFF' }}>Click me</Text>
192
- </Frame>
193
- )
118
+ ### Export
194
119
 
195
- export default () => (
196
- <Frame style={{ gap: 16, flexDirection: 'column' }}>
197
- <Button /> {/* Creates Component */}
198
- <Button /> {/* Creates Instance */}
199
- <Button /> {/* Creates Instance */}
200
- </Frame>
201
- )
120
+ ```bash
121
+ figma-use export node <id> --output design.png
122
+ figma-use export screenshot --output viewport.png
123
+ figma-use export selection --output selection.png
202
124
  ```
203
125
 
204
- **defineComponentSet** — component with variants:
205
- ```tsx
206
- import { defineComponentSet, Frame, Text } from '@dannote/figma-use/render'
207
-
208
- const Button = defineComponentSet('Button', {
209
- variant: ['Primary', 'Secondary'] as const,
210
- size: ['Small', 'Large'] as const,
211
- }, ({ variant, size }) => (
212
- <Frame style={{
213
- padding: size === 'Large' ? 16 : 8,
214
- backgroundColor: variant === 'Primary' ? '#3B82F6' : '#E5E7EB',
215
- borderRadius: 8,
216
- flexDirection: 'row',
217
- justifyContent: 'center',
218
- }}>
219
- <Text style={{
220
- fontSize: size === 'Large' ? 16 : 14,
221
- color: variant === 'Primary' ? '#FFF' : '#111',
222
- }}>
223
- {variant} Button
224
- </Text>
225
- </Frame>
226
- ))
126
+ ### Navigate
227
127
 
228
- export default () => (
229
- <Frame style={{ gap: 16, flexDirection: 'column' }}>
230
- <Button variant="Primary" size="Large" />
231
- <Button variant="Primary" size="Small" />
232
- <Button variant="Secondary" size="Large" />
233
- </Frame>
234
- )
128
+ ```bash
129
+ figma-use page list
130
+ figma-use page set "Page Name"
131
+ figma-use viewport zoom-to-fit <ids...>
235
132
  ```
236
133
 
237
- Creates a real Figma ComponentSet with all variant combinations.
238
-
239
- #### Variable Bindings (Experimental)
240
-
241
- Bind Figma variables to colors by name with fallback values:
242
-
243
- ```tsx
244
- import { defineVars, Frame } from '@dannote/figma-use'
134
+ ### Variables & Styles
245
135
 
246
- const colors = defineVars({
247
- primary: { name: 'Colors/Gray/50', value: '#F8FAFC' },
248
- border: { name: 'Colors/Gray/500', value: '#64748B' },
249
- })
250
-
251
- export default () => (
252
- <Frame style={{ backgroundColor: colors.primary, borderColor: colors.border }} />
253
- )
136
+ ```bash
137
+ figma-use variable list
138
+ figma-use variable create "Primary" --collection <id> --type COLOR --value "#3B82F6"
139
+ figma-use style list
140
+ figma-use style create-paint "Brand/Primary" --color "#E11D48"
254
141
  ```
255
142
 
256
- Supports: `backgroundColor`, `borderColor`, text `color`.
257
-
258
- ### Eval (Arbitrary Code)
143
+ ### Escape Hatch
259
144
 
260
145
  ```bash
261
146
  figma-use eval "return figma.currentPage.name"
262
- figma-use eval "const r = figma.createRectangle(); r.resize(100, 100); return r.id"
147
+ figma-use eval "figma.createRectangle().resize(100, 100)"
263
148
  ```
264
149
 
150
+ ---
151
+
265
152
  ## Output
266
153
 
267
- Human-readable by default. Add `--json` for machine parsing:
268
- ```bash
269
- figma-use node get <id> --json
270
- ```
154
+ Human-readable by default. Add `--json` for machine parsing.
271
155
 
272
156
  ## Colors
273
157
 
274
- Hex format: `#RGB`, `#RRGGBB`, or `#RRGGBBAA`
158
+ Hex format: `#RGB`, `#RRGGBB`, `#RRGGBBAA`
275
159
 
276
160
  ## Node IDs
277
161
 
278
- Format: `pageIndex:nodeIndex` (e.g., `1:2`, `45:123`)
279
-
280
- Get IDs from:
281
- - `figma-use selection get`
282
- - `figma-use node children <parentId>`
283
- - Figma → right-click → Copy link → ID in URL
162
+ Format: `sessionID:localID` (e.g., `1:2`, `45:123`). Get from `figma-use selection get` or `figma-use node tree`.
package/dist/cli/index.js CHANGED
@@ -22839,16 +22839,49 @@ async function getParentGUID() {
22839
22839
 
22840
22840
  // packages/cli/src/commands/status.ts
22841
22841
  var status_default = defineCommand({
22842
- meta: { description: "Check if plugin is connected" },
22842
+ meta: { description: "Check connection status (plugin, DevTools, multiplayer)" },
22843
22843
  args: { json: { type: "boolean", description: "Output as JSON" } },
22844
22844
  async run({ args }) {
22845
+ const result = {
22846
+ proxy: false,
22847
+ plugin: false,
22848
+ devtools: false,
22849
+ fileKey: null,
22850
+ multiplayer: false
22851
+ };
22845
22852
  try {
22846
22853
  const status = await getStatus();
22847
- console.log(status.pluginConnected ? "\u2713 Plugin connected" : "\u2717 Plugin not connected");
22848
- } catch {
22849
- console.log("\u2717 Proxy not running");
22850
- process.exit(1);
22854
+ result.proxy = true;
22855
+ result.plugin = status.pluginConnected;
22856
+ } catch {}
22857
+ try {
22858
+ result.fileKey = await getFileKey();
22859
+ result.devtools = true;
22860
+ result.multiplayer = true;
22861
+ } catch {}
22862
+ if (args.json) {
22863
+ console.log(JSON.stringify(result, null, 2));
22864
+ return;
22851
22865
  }
22866
+ console.log(result.proxy ? "\u2713 Proxy running" : "\u2717 Proxy not running (run: figma-use proxy)");
22867
+ console.log(result.plugin ? "\u2713 Plugin connected" : "\u2717 Plugin not connected (open plugin in Figma)");
22868
+ console.log(result.devtools ? "\u2713 DevTools available" : "\u2717 DevTools not available (run: figma --remote-debugging-port=9222)");
22869
+ if (result.devtools) {
22870
+ if (result.fileKey) {
22871
+ console.log(`\u2713 Figma file ready (${result.fileKey})`);
22872
+ } else {
22873
+ console.log("\u2717 No Figma file open (open a file from figma.com, not local)");
22874
+ }
22875
+ }
22876
+ if (result.proxy && result.plugin && result.multiplayer) {
22877
+ console.log(`
22878
+ \u2713 Ready for render command`);
22879
+ } else if (result.proxy && result.plugin) {
22880
+ console.log(`
22881
+ \u26A0 CLI commands work, but render requires DevTools + Figma file`);
22882
+ }
22883
+ if (!result.proxy)
22884
+ process.exit(1);
22852
22885
  }
22853
22886
  });
22854
22887
  // packages/cli/src/commands/proxy.ts
@@ -4637,22 +4637,6 @@ var init_prompt = __esm(() => {
4637
4637
  });
4638
4638
 
4639
4639
  // packages/cli/src/multiplayer/protocol.ts
4640
- function parseVarint(data, pos) {
4641
- let value = 0;
4642
- let shift = 0;
4643
- while (pos < data.length) {
4644
- const byte2 = data[pos];
4645
- if (byte2 === undefined)
4646
- break;
4647
- pos++;
4648
- value |= (byte2 & KIWI.VARINT_VALUE_MASK) << shift;
4649
- if (!(byte2 & KIWI.VARINT_CONTINUE_BIT)) {
4650
- break;
4651
- }
4652
- shift += KIWI.VARINT_BITS_PER_BYTE;
4653
- }
4654
- return [value, pos];
4655
- }
4656
4640
  function isKiwiMessage(data) {
4657
4641
  return data.length >= 2 && data[0] === KIWI.MESSAGE_MARKER;
4658
4642
  }
@@ -4684,7 +4668,7 @@ function buildMultiplayerUrl(fileKey, trackingId) {
4684
4668
  });
4685
4669
  return `wss://www.figma.com/api/multiplayer/${fileKey}?${params}`;
4686
4670
  }
4687
- var MESSAGE_TYPES, ZSTD_MAGIC, KIWI, SESSION_ID, FIG_WIRE_MAGIC = "fig-wire", PROTOCOL_VERSION = 151;
4671
+ var MESSAGE_TYPES, ZSTD_MAGIC, KIWI, FIG_WIRE_MAGIC = "fig-wire", PROTOCOL_VERSION = 151;
4688
4672
  var init_protocol = __esm(() => {
4689
4673
  MESSAGE_TYPES = {
4690
4674
  JOIN_START: 0,
@@ -4711,10 +4695,6 @@ var init_protocol = __esm(() => {
4711
4695
  VARINT_VALUE_MASK: 127,
4712
4696
  VARINT_BITS_PER_BYTE: 7
4713
4697
  };
4714
- SESSION_ID = {
4715
- MIN: 1e4,
4716
- MAX: 1e6
4717
- };
4718
4698
  });
4719
4699
 
4720
4700
  // node_modules/kiwi-schema/kiwi-esm.js
@@ -11868,12 +11848,6 @@ class FigmaMultiplayerClient {
11868
11848
  if (!isKiwiMessage(decompressed))
11869
11849
  return;
11870
11850
  const msgType = getKiwiMessageType(decompressed);
11871
- if (msgType === MESSAGE_TYPES.JOIN_START && decompressed[2] === KIWI.SESSION_ID_FIELD) {
11872
- const [val] = parseVarint(decompressed, 3);
11873
- if (val > SESSION_ID.MIN && val < SESSION_ID.MAX) {
11874
- sessionID = val;
11875
- }
11876
- }
11877
11851
  if (msgType === MESSAGE_TYPES.JOIN_END) {
11878
11852
  joinEndReceived = true;
11879
11853
  }
@@ -11890,10 +11864,10 @@ class FigmaMultiplayerClient {
11890
11864
  this.options.onMessage(message);
11891
11865
  } catch {}
11892
11866
  }
11893
- if (sessionID && reconnectSequenceNumber && joinEndReceived && (this.state === "connecting" || this.state === "connected")) {
11867
+ if (joinEndReceived && (this.state === "connecting" || this.state === "connected")) {
11894
11868
  clearTimeout(timeout);
11895
11869
  this.state = "ready";
11896
- this.sessionInfo = { sessionID, reconnectSequenceNumber };
11870
+ this.sessionInfo = { sessionID: sessionID || 0, reconnectSequenceNumber };
11897
11871
  resolve(this.sessionInfo);
11898
11872
  }
11899
11873
  } catch {}
@@ -11967,6 +11941,9 @@ class FigmaMultiplayerClient {
11967
11941
  isReady() {
11968
11942
  return this.state === "ready";
11969
11943
  }
11944
+ isConnected() {
11945
+ return this.state === "connected" && this.ws !== null && this.ws.readyState === WebSocket.OPEN;
11946
+ }
11970
11947
  close() {
11971
11948
  if (this.ws) {
11972
11949
  this.ws.close();
@@ -26821,11 +26798,23 @@ function cleanupIdleConnections() {
26821
26798
  }
26822
26799
  async function getMultiplayerConnection(fileKey) {
26823
26800
  await ensureInitialized();
26801
+ for (const [key, conn] of connectionPool) {
26802
+ if (key !== fileKey) {
26803
+ consola.info(`Closing stale connection to ${key} (switched to ${fileKey})`);
26804
+ conn.client.close();
26805
+ connectionPool.delete(key);
26806
+ }
26807
+ }
26824
26808
  const existing = connectionPool.get(fileKey);
26825
26809
  if (existing) {
26826
- existing.lastUsed = Date.now();
26827
- consola.debug(`Reusing multiplayer connection for ${fileKey}`);
26828
- return { client: existing.client, sessionID: existing.sessionID };
26810
+ if (existing.client.isConnected()) {
26811
+ existing.lastUsed = Date.now();
26812
+ consola.debug(`Reusing multiplayer connection for ${fileKey}`);
26813
+ return { client: existing.client, sessionID: existing.sessionID };
26814
+ } else {
26815
+ consola.warn(`Multiplayer connection to ${fileKey} died, reconnecting...`);
26816
+ connectionPool.delete(fileKey);
26817
+ }
26829
26818
  }
26830
26819
  consola.info(`Creating multiplayer connection for ${fileKey}`);
26831
26820
  const cookies = await getCookiesFromDevTools2();
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "@dannote/figma-use",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Control Figma from the command line. Full read/write access for AI agents.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "figma-use": "bin/figma-use.js"
8
8
  },
9
+ "exports": {
10
+ ".": "./dist/cli/index.js",
11
+ "./render": "./packages/cli/src/render/index.ts",
12
+ "./components": "./packages/cli/src/render/components.tsx"
13
+ },
9
14
  "scripts": {
10
15
  "build": "bun run build:proxy && bun run build:cli && bun run build:plugin",
11
16
  "build:proxy": "bun build packages/proxy/src/index.ts --outdir dist/proxy --target bun",
@@ -18,6 +23,7 @@
18
23
  "files": [
19
24
  "bin",
20
25
  "dist",
26
+ "packages/cli/src/render",
21
27
  "packages/plugin/dist",
22
28
  "README.md",
23
29
  "CHANGELOG.md",