@dannote/figma-use 0.5.1 → 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 +8 -0
- package/SKILL.md +26 -3
- package/dist/cli/index.js +38 -5
- package/dist/proxy/index.js +21 -32
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ 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
|
+
|
|
10
18
|
## [0.5.1] - 2026-01-18
|
|
11
19
|
|
|
12
20
|
### Added
|
package/SKILL.md
CHANGED
|
@@ -36,7 +36,7 @@ figma-use plugin
|
|
|
36
36
|
|
|
37
37
|
## JSX Rendering (Fastest Way)
|
|
38
38
|
|
|
39
|
-
For complex layouts, use `render --stdin` with JSX.
|
|
39
|
+
For complex layouts, use `render --stdin` with pure JSX. No imports needed — elements are built-in:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
echo '<Frame style={{padding: 24, gap: 16, flexDirection: "column", backgroundColor: "#FFF", borderRadius: 12}}>
|
|
@@ -45,11 +45,34 @@ echo '<Frame style={{padding: 24, gap: 16, flexDirection: "column", backgroundCo
|
|
|
45
45
|
</Frame>' | figma-use render --stdin
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
**Elements:** `Frame`, `Rectangle`, `Ellipse`, `Text`, `Line`, `Star`, `Polygon`, `Vector`, `Group`
|
|
49
|
+
|
|
48
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`
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
### Buttons Example (3 sizes)
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
```bash
|
|
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
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Advanced: Components & Variants
|
|
69
|
+
|
|
70
|
+
For `defineComponent`, `defineComponentSet`, `defineVars` — use files with imports:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
figma-use render --examples # Full API reference
|
|
74
|
+
figma-use render ./MyComponent.figma.tsx
|
|
75
|
+
```
|
|
53
76
|
|
|
54
77
|
---
|
|
55
78
|
|
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
|
|
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
|
-
|
|
22848
|
-
|
|
22849
|
-
|
|
22850
|
-
|
|
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
|
package/dist/proxy/index.js
CHANGED
|
@@ -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,
|
|
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 (
|
|
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.
|
|
26827
|
-
|
|
26828
|
-
|
|
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();
|