@seed-design/mcp 0.0.6 → 0.0.15
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/bin/main.mjs +16904 -0
- package/bin/socket.mjs +155 -0
- package/package.json +8 -3
- package/src/bin/main.ts +52 -0
- package/src/bin/socket.ts +194 -0
- package/src/logger.ts +32 -34
- package/src/prompts.ts +27 -0
- package/src/responses.ts +90 -0
- package/src/tools.ts +436 -0
- package/src/types.ts +67 -0
- package/src/websocket.ts +248 -0
- package/bin/index.mjs +0 -4964
- package/src/bin/index.ts +0 -34
- package/src/config.ts +0 -166
- package/src/figma.ts +0 -211
- package/src/index.ts +0 -36
- package/src/save-image.ts +0 -16
- package/src/server.ts +0 -288
package/src/tools.ts
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import {
|
|
3
|
+
createRestNormalizer,
|
|
4
|
+
generateCode,
|
|
5
|
+
generateFigmaSummary,
|
|
6
|
+
getFigmaColorVariableNames,
|
|
7
|
+
} from "@seed-design/figma";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import {
|
|
10
|
+
formatErrorResponse,
|
|
11
|
+
formatImageResponse,
|
|
12
|
+
formatObjectResponse,
|
|
13
|
+
formatTextResponse,
|
|
14
|
+
} from "./responses";
|
|
15
|
+
import type { FigmaWebSocketClient } from "./websocket";
|
|
16
|
+
|
|
17
|
+
export function registerTools(server: McpServer, figmaClient: FigmaWebSocketClient): void {
|
|
18
|
+
const { joinChannel, sendCommandToFigma } = figmaClient;
|
|
19
|
+
|
|
20
|
+
// join_channel tool
|
|
21
|
+
server.tool(
|
|
22
|
+
"join_channel",
|
|
23
|
+
"Join a specific channel to communicate with Figma",
|
|
24
|
+
{
|
|
25
|
+
channel: z.string().describe("The name of the channel to join").default(""),
|
|
26
|
+
},
|
|
27
|
+
async ({ channel }) => {
|
|
28
|
+
try {
|
|
29
|
+
if (!channel) {
|
|
30
|
+
// If no channel provided, ask the user for input
|
|
31
|
+
return {
|
|
32
|
+
...formatTextResponse("Please provide a channel name to join:"),
|
|
33
|
+
followUp: {
|
|
34
|
+
tool: "join_channel",
|
|
35
|
+
description: "Join the specified channel",
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await joinChannel(channel);
|
|
41
|
+
return formatTextResponse(`Successfully joined channel: ${channel}`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return formatErrorResponse("join_channel", error);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Document Info Tool
|
|
49
|
+
server.tool(
|
|
50
|
+
"get_document_info",
|
|
51
|
+
"Get detailed information about the current Figma document",
|
|
52
|
+
{},
|
|
53
|
+
async () => {
|
|
54
|
+
try {
|
|
55
|
+
const result = await sendCommandToFigma("get_document_info");
|
|
56
|
+
return formatObjectResponse(result);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return formatErrorResponse("get_document_info", error);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// Selection Tool
|
|
64
|
+
server.tool(
|
|
65
|
+
"get_selection",
|
|
66
|
+
"Get information about the current selection in Figma",
|
|
67
|
+
{},
|
|
68
|
+
async () => {
|
|
69
|
+
try {
|
|
70
|
+
const result = await sendCommandToFigma("get_selection");
|
|
71
|
+
return formatObjectResponse(result);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return formatErrorResponse("get_selection", error);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Annotation Tool
|
|
79
|
+
server.tool(
|
|
80
|
+
"add_annotations",
|
|
81
|
+
"Add annotations to multiple nodes in Figma",
|
|
82
|
+
{
|
|
83
|
+
annotations: z.array(
|
|
84
|
+
z.object({
|
|
85
|
+
nodeId: z.string().describe("The ID of the node to add an annotation to"),
|
|
86
|
+
labelMarkdown: z
|
|
87
|
+
.string()
|
|
88
|
+
.describe("The markdown label for the annotation, do not escape newlines"),
|
|
89
|
+
}),
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
async ({ annotations }) => {
|
|
93
|
+
try {
|
|
94
|
+
await sendCommandToFigma("add_annotations", { annotations });
|
|
95
|
+
|
|
96
|
+
return formatTextResponse(
|
|
97
|
+
`Annotations added to nodes ${annotations.map((annotation) => annotation.nodeId).join(", ")}`,
|
|
98
|
+
);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return formatErrorResponse("add_annotations", error);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// Get Annotations Tool
|
|
106
|
+
server.tool(
|
|
107
|
+
"get_annotations",
|
|
108
|
+
"Get annotations for a specific node in Figma",
|
|
109
|
+
{ nodeId: z.string().describe("The ID of the node to get annotations for") },
|
|
110
|
+
async ({ nodeId }) => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await sendCommandToFigma("get_annotations", { nodeId });
|
|
113
|
+
return formatObjectResponse(result);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return formatErrorResponse("get_annotations", error);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Node Info Tool
|
|
121
|
+
server.tool(
|
|
122
|
+
"get_node_info",
|
|
123
|
+
"Get detailed information about a specific node in Figma",
|
|
124
|
+
{
|
|
125
|
+
nodeId: z.string().describe("The ID of the node to get information about"),
|
|
126
|
+
},
|
|
127
|
+
async ({ nodeId }) => {
|
|
128
|
+
try {
|
|
129
|
+
const result: any = await sendCommandToFigma("get_node_info", { nodeId });
|
|
130
|
+
const normalizer = createRestNormalizer(result);
|
|
131
|
+
const node = normalizer(result.document);
|
|
132
|
+
|
|
133
|
+
const original =
|
|
134
|
+
generateFigmaSummary(node, {
|
|
135
|
+
shouldPrintSource: true,
|
|
136
|
+
shouldInferVariableName: false,
|
|
137
|
+
shouldInferAutoLayout: false,
|
|
138
|
+
}) ?? "Failed to generate summarized node info";
|
|
139
|
+
const inferred =
|
|
140
|
+
generateFigmaSummary(node, {
|
|
141
|
+
shouldPrintSource: true,
|
|
142
|
+
shouldInferVariableName: false,
|
|
143
|
+
shouldInferAutoLayout: true,
|
|
144
|
+
}) ?? "Failed to generate summarized node info";
|
|
145
|
+
|
|
146
|
+
return formatObjectResponse({
|
|
147
|
+
original: {
|
|
148
|
+
data: original,
|
|
149
|
+
description: "Original Figma node info",
|
|
150
|
+
},
|
|
151
|
+
inferred: {
|
|
152
|
+
data: inferred,
|
|
153
|
+
description: "AutoLayout Inferred Figma node info (fix suggestions)",
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
} catch (error) {
|
|
157
|
+
return formatErrorResponse("get_node_info", error);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Nodes Info Tool
|
|
163
|
+
server.tool(
|
|
164
|
+
"get_nodes_info",
|
|
165
|
+
"Get detailed information about multiple nodes in Figma",
|
|
166
|
+
{
|
|
167
|
+
nodeIds: z.array(z.string()).describe("Array of node IDs to get information about"),
|
|
168
|
+
},
|
|
169
|
+
async ({ nodeIds }) => {
|
|
170
|
+
try {
|
|
171
|
+
const results = await Promise.all(
|
|
172
|
+
nodeIds.map(async (nodeId) => {
|
|
173
|
+
const result: any = await sendCommandToFigma("get_node_info", { nodeId });
|
|
174
|
+
const normalizer = createRestNormalizer(result);
|
|
175
|
+
const node = normalizer(result.document);
|
|
176
|
+
|
|
177
|
+
const original =
|
|
178
|
+
generateFigmaSummary(node, {
|
|
179
|
+
shouldInferVariableName: false,
|
|
180
|
+
shouldPrintSource: true,
|
|
181
|
+
shouldInferAutoLayout: false,
|
|
182
|
+
}) ?? "Failed to generate summarized node info";
|
|
183
|
+
const inferred =
|
|
184
|
+
generateFigmaSummary(node, {
|
|
185
|
+
shouldInferVariableName: true,
|
|
186
|
+
shouldPrintSource: true,
|
|
187
|
+
shouldInferAutoLayout: false,
|
|
188
|
+
}) ?? "Failed to generate summarized node info";
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
nodeId,
|
|
192
|
+
original: {
|
|
193
|
+
data: original,
|
|
194
|
+
description: "Original Figma node info",
|
|
195
|
+
},
|
|
196
|
+
inferred: {
|
|
197
|
+
data: inferred,
|
|
198
|
+
description: "AutoLayout Inferred Figma node info (fix suggestions)",
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
203
|
+
return formatObjectResponse(results);
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return formatErrorResponse("get_nodes_info", error);
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
server.tool(
|
|
211
|
+
"get_node_react_code",
|
|
212
|
+
"Get the React code for a specific node in Figma",
|
|
213
|
+
{ nodeId: z.string().describe("The ID of the node to get code for") },
|
|
214
|
+
async ({ nodeId }) => {
|
|
215
|
+
try {
|
|
216
|
+
const result: any = await sendCommandToFigma("get_node_info", { nodeId });
|
|
217
|
+
const normalizer = createRestNormalizer(result);
|
|
218
|
+
|
|
219
|
+
const code =
|
|
220
|
+
generateCode(normalizer(result.document), {
|
|
221
|
+
shouldInferVariableName: true,
|
|
222
|
+
shouldPrintSource: false,
|
|
223
|
+
shouldInferAutoLayout: true,
|
|
224
|
+
}) ?? "Failed to generate code";
|
|
225
|
+
|
|
226
|
+
return formatTextResponse(code);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return formatErrorResponse("get_node_react_code", error);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Clone Node Tool
|
|
234
|
+
server.tool(
|
|
235
|
+
"clone_node",
|
|
236
|
+
"Clone an existing node in Figma",
|
|
237
|
+
{
|
|
238
|
+
nodeId: z.string().describe("The ID of the node to clone"),
|
|
239
|
+
x: z.number().optional().describe("New X position for the clone"),
|
|
240
|
+
y: z.number().optional().describe("New Y position for the clone"),
|
|
241
|
+
},
|
|
242
|
+
async ({ nodeId, x, y }) => {
|
|
243
|
+
try {
|
|
244
|
+
const result = await sendCommandToFigma("clone_node", { nodeId, x, y });
|
|
245
|
+
const typedResult = result as { name: string; id: string };
|
|
246
|
+
return formatTextResponse(
|
|
247
|
+
`Cloned node "${typedResult.name}" with new ID: ${typedResult.id}${x !== undefined && y !== undefined ? ` at position (${x}, ${y})` : ""}`,
|
|
248
|
+
);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
return formatErrorResponse("clone_node", error);
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Export Node as Image Tool
|
|
256
|
+
server.tool(
|
|
257
|
+
"export_node_as_image",
|
|
258
|
+
"Export a node as an image from Figma",
|
|
259
|
+
{
|
|
260
|
+
nodeId: z.string().describe("The ID of the node to export"),
|
|
261
|
+
format: z.enum(["PNG", "JPG", "SVG", "PDF"]).optional().describe("Export format"),
|
|
262
|
+
scale: z.number().positive().optional().describe("Export scale"),
|
|
263
|
+
},
|
|
264
|
+
async ({ nodeId, format, scale }) => {
|
|
265
|
+
try {
|
|
266
|
+
const result = await sendCommandToFigma("export_node_as_image", {
|
|
267
|
+
nodeId,
|
|
268
|
+
format: format || "PNG",
|
|
269
|
+
scale: scale || 1,
|
|
270
|
+
});
|
|
271
|
+
const typedResult = result as { imageData: string; mimeType: string };
|
|
272
|
+
return formatImageResponse(typedResult.imageData, typedResult.mimeType || "image/png");
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return formatErrorResponse("export_node_as_image", error);
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
// Retrieve Color Variable Names Tool
|
|
280
|
+
server.tool(
|
|
281
|
+
"retrieve_color_variable_names",
|
|
282
|
+
"Retrieve available color variable names in scope",
|
|
283
|
+
{
|
|
284
|
+
scope: z
|
|
285
|
+
.enum(["fg", "bg", "stroke", "palette"])
|
|
286
|
+
.array()
|
|
287
|
+
.describe("The scope of the color variable names to retrieve"),
|
|
288
|
+
},
|
|
289
|
+
async ({ scope }) => {
|
|
290
|
+
try {
|
|
291
|
+
const result = getFigmaColorVariableNames(scope);
|
|
292
|
+
return formatObjectResponse(result);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
return formatErrorResponse("retrieve_color_variable_names", error);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
server.tool(
|
|
300
|
+
"set_fill_color",
|
|
301
|
+
"Set the fill color of a node",
|
|
302
|
+
{
|
|
303
|
+
nodeId: z.string().describe("The ID of the node to set the fill color of"),
|
|
304
|
+
colorToken: z
|
|
305
|
+
.string()
|
|
306
|
+
.describe(
|
|
307
|
+
"The color token to set the fill color to. Format: `{category}/{name}`. Example: `bg/brand`",
|
|
308
|
+
),
|
|
309
|
+
},
|
|
310
|
+
async ({ nodeId, colorToken }) => {
|
|
311
|
+
try {
|
|
312
|
+
await sendCommandToFigma("set_fill_color", { nodeId, colorToken });
|
|
313
|
+
return formatTextResponse(`Fill color set to ${colorToken}`);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
return formatErrorResponse("set_fill_color", error);
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
server.tool(
|
|
321
|
+
"set_stroke_color",
|
|
322
|
+
"Set the stroke color of a node",
|
|
323
|
+
{
|
|
324
|
+
nodeId: z.string().describe("The ID of the node to set the stroke color of"),
|
|
325
|
+
colorToken: z
|
|
326
|
+
.string()
|
|
327
|
+
.describe(
|
|
328
|
+
"The color token to set the stroke color to. Format: `{category}/{name}`. Example: `stroke/neutral`",
|
|
329
|
+
),
|
|
330
|
+
},
|
|
331
|
+
async ({ nodeId, colorToken }) => {
|
|
332
|
+
try {
|
|
333
|
+
await sendCommandToFigma("set_stroke_color", { nodeId, colorToken });
|
|
334
|
+
return formatTextResponse(`Stroke color set to ${colorToken}`);
|
|
335
|
+
} catch (error) {
|
|
336
|
+
return formatErrorResponse("set_stroke_color", error);
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
server.tool(
|
|
342
|
+
"set_auto_layout",
|
|
343
|
+
"Set the auto layout of a node",
|
|
344
|
+
{
|
|
345
|
+
nodeId: z.string().describe("The ID of the node to set the auto layout of"),
|
|
346
|
+
layoutMode: z.enum(["HORIZONTAL", "VERTICAL"]).optional().describe("The layout mode to set"),
|
|
347
|
+
layoutWrap: z.enum(["NO_WRAP", "WRAP"]).optional().describe("The layout wrap to set"),
|
|
348
|
+
primaryAxisAlignItems: z
|
|
349
|
+
.enum(["MIN", "MAX", "CENTER", "SPACE_BETWEEN"])
|
|
350
|
+
.optional()
|
|
351
|
+
.describe("The primary axis align items to set"),
|
|
352
|
+
counterAxisAlignItems: z
|
|
353
|
+
.enum(["MIN", "MAX", "CENTER", "BASELINE"])
|
|
354
|
+
.optional()
|
|
355
|
+
.describe("The counter axis align items to set"),
|
|
356
|
+
itemSpacing: z.number().optional().describe("The item spacing to set"),
|
|
357
|
+
horizontalPadding: z.number().optional().describe("The horizontal padding to set"),
|
|
358
|
+
verticalPadding: z.number().optional().describe("The vertical padding to set"),
|
|
359
|
+
paddingLeft: z.number().optional().describe("The padding left to set (when left != right)"),
|
|
360
|
+
paddingRight: z.number().optional().describe("The padding right to set (when left != right)"),
|
|
361
|
+
paddingTop: z.number().optional().describe("The padding top to set (when top != bottom)"),
|
|
362
|
+
paddingBottom: z
|
|
363
|
+
.number()
|
|
364
|
+
.optional()
|
|
365
|
+
.describe("The padding bottom to set (when top != bottom)"),
|
|
366
|
+
},
|
|
367
|
+
async ({
|
|
368
|
+
nodeId,
|
|
369
|
+
layoutMode,
|
|
370
|
+
layoutWrap,
|
|
371
|
+
primaryAxisAlignItems,
|
|
372
|
+
counterAxisAlignItems,
|
|
373
|
+
itemSpacing,
|
|
374
|
+
horizontalPadding,
|
|
375
|
+
verticalPadding,
|
|
376
|
+
paddingLeft,
|
|
377
|
+
paddingRight,
|
|
378
|
+
paddingTop,
|
|
379
|
+
paddingBottom,
|
|
380
|
+
}) => {
|
|
381
|
+
try {
|
|
382
|
+
await sendCommandToFigma("set_auto_layout", {
|
|
383
|
+
nodeId,
|
|
384
|
+
layoutMode,
|
|
385
|
+
layoutWrap,
|
|
386
|
+
primaryAxisAlignItems,
|
|
387
|
+
counterAxisAlignItems,
|
|
388
|
+
itemSpacing,
|
|
389
|
+
horizontalPadding,
|
|
390
|
+
verticalPadding,
|
|
391
|
+
paddingLeft,
|
|
392
|
+
paddingRight,
|
|
393
|
+
paddingTop,
|
|
394
|
+
paddingBottom,
|
|
395
|
+
});
|
|
396
|
+
return formatTextResponse(`Layout set to ${layoutMode}`);
|
|
397
|
+
} catch (error) {
|
|
398
|
+
return formatErrorResponse("set_auto_layout", error);
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
server.tool(
|
|
404
|
+
"set_size",
|
|
405
|
+
"Set the size of a node",
|
|
406
|
+
{
|
|
407
|
+
nodeId: z.string().describe("The ID of the node to set the size of"),
|
|
408
|
+
layoutSizingHorizontal: z
|
|
409
|
+
.enum(["HUG", "FILL"])
|
|
410
|
+
.optional()
|
|
411
|
+
.describe("The horizontal layout sizing to set (exclusive with width)"),
|
|
412
|
+
layoutSizingVertical: z
|
|
413
|
+
.enum(["HUG", "FILL"])
|
|
414
|
+
.optional()
|
|
415
|
+
.describe("The vertical layout sizing to set (exclusive with height)"),
|
|
416
|
+
width: z.number().optional().describe("The width to set (raw value)"),
|
|
417
|
+
height: z.number().optional().describe("The height to set (raw value)"),
|
|
418
|
+
},
|
|
419
|
+
async ({ nodeId, layoutSizingHorizontal, layoutSizingVertical, width, height }) => {
|
|
420
|
+
try {
|
|
421
|
+
await sendCommandToFigma("set_size", {
|
|
422
|
+
nodeId,
|
|
423
|
+
layoutSizingHorizontal,
|
|
424
|
+
layoutSizingVertical,
|
|
425
|
+
width,
|
|
426
|
+
height,
|
|
427
|
+
});
|
|
428
|
+
return formatTextResponse(
|
|
429
|
+
`Size set to ${width ?? layoutSizingHorizontal}x${height ?? layoutSizingVertical}`,
|
|
430
|
+
);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return formatErrorResponse("set_size", error);
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
);
|
|
436
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// TypeScript interfaces for Figma API communication
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Basic response from Figma WebSocket API
|
|
5
|
+
*/
|
|
6
|
+
export interface FigmaResponse {
|
|
7
|
+
id: string;
|
|
8
|
+
result?: any;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Progress update for long-running commands
|
|
14
|
+
*/
|
|
15
|
+
export interface CommandProgressUpdate {
|
|
16
|
+
type: "command_progress";
|
|
17
|
+
commandId: string;
|
|
18
|
+
commandType: string;
|
|
19
|
+
status: "started" | "in_progress" | "completed" | "error";
|
|
20
|
+
progress: number;
|
|
21
|
+
totalItems: number;
|
|
22
|
+
processedItems: number;
|
|
23
|
+
currentChunk?: number;
|
|
24
|
+
totalChunks?: number;
|
|
25
|
+
chunkSize?: number;
|
|
26
|
+
message: string;
|
|
27
|
+
payload?: any;
|
|
28
|
+
timestamp: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Command types supported by the Figma plugin
|
|
33
|
+
*/
|
|
34
|
+
export type FigmaCommand =
|
|
35
|
+
| "get_document_info"
|
|
36
|
+
| "get_selection"
|
|
37
|
+
| "get_node_info"
|
|
38
|
+
| "get_nodes_info"
|
|
39
|
+
| "export_node_as_image"
|
|
40
|
+
| "join"
|
|
41
|
+
| "clone_node"
|
|
42
|
+
| "add_annotations"
|
|
43
|
+
| "get_annotations"
|
|
44
|
+
| "set_fill_color"
|
|
45
|
+
| "set_stroke_color"
|
|
46
|
+
| "set_auto_layout"
|
|
47
|
+
| "set_size";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Pending request information
|
|
51
|
+
*/
|
|
52
|
+
export interface PendingRequest {
|
|
53
|
+
resolve: (value: unknown) => void;
|
|
54
|
+
reject: (reason: unknown) => void;
|
|
55
|
+
timeout: ReturnType<typeof setTimeout>;
|
|
56
|
+
lastActivity: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* WebSocket message from Figma
|
|
61
|
+
*/
|
|
62
|
+
export interface FigmaWebSocketMessage {
|
|
63
|
+
message: FigmaResponse | any;
|
|
64
|
+
type?: string;
|
|
65
|
+
id?: string;
|
|
66
|
+
[key: string]: any;
|
|
67
|
+
}
|