@dannote/figma-use 0.1.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.
@@ -0,0 +1,996 @@
1
+ "use strict";
2
+ (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
22
+ var __async = (__this, __arguments, generator) => {
23
+ return new Promise((resolve, reject) => {
24
+ var fulfilled = (value) => {
25
+ try {
26
+ step(generator.next(value));
27
+ } catch (e) {
28
+ reject(e);
29
+ }
30
+ };
31
+ var rejected = (value) => {
32
+ try {
33
+ step(generator.throw(value));
34
+ } catch (e) {
35
+ reject(e);
36
+ }
37
+ };
38
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
39
+ step((generator = generator.apply(__this, __arguments)).next());
40
+ });
41
+ };
42
+
43
+ // src/main.ts
44
+ console.log("[Figma Bridge] Plugin main loaded at", (/* @__PURE__ */ new Date()).toISOString());
45
+ figma.showUI(__html__, { width: 300, height: 200 });
46
+ figma.ui.onmessage = (msg) => __async(null, null, function* () {
47
+ if (msg.type !== "command") return;
48
+ try {
49
+ yield figma.loadAllPagesAsync();
50
+ const result = yield handleCommand(msg.command, msg.args);
51
+ figma.ui.postMessage({ type: "result", id: msg.id, result });
52
+ } catch (error) {
53
+ figma.ui.postMessage({ type: "result", id: msg.id, error: String(error) });
54
+ }
55
+ });
56
+ function handleCommand(command, args) {
57
+ return __async(this, null, function* () {
58
+ switch (command) {
59
+ // ==================== READ ====================
60
+ case "get-selection":
61
+ return figma.currentPage.selection.map(serializeNode);
62
+ case "get-node-info": {
63
+ const { id } = args;
64
+ const node = yield figma.getNodeByIdAsync(id);
65
+ return node ? serializeNode(node) : null;
66
+ }
67
+ case "get-all-components": {
68
+ const components = [];
69
+ figma.root.findAll((node) => {
70
+ if (node.type === "COMPONENT" || node.type === "COMPONENT_SET") {
71
+ components.push(serializeNode(node));
72
+ }
73
+ return false;
74
+ });
75
+ return components;
76
+ }
77
+ case "get-pages":
78
+ return figma.root.children.map((page) => ({ id: page.id, name: page.name }));
79
+ case "create-page": {
80
+ const { name } = args;
81
+ const page = figma.createPage();
82
+ page.name = name;
83
+ return { id: page.id, name: page.name };
84
+ }
85
+ case "set-current-page": {
86
+ const { id } = args;
87
+ const page = yield figma.getNodeByIdAsync(id);
88
+ if (!page || page.type !== "PAGE") throw new Error("Page not found");
89
+ yield figma.setCurrentPageAsync(page);
90
+ return { id: page.id, name: page.name };
91
+ }
92
+ case "get-local-styles": {
93
+ const { type } = args || {};
94
+ const result = {};
95
+ if (!type || type === "all" || type === "paint") {
96
+ const styles = yield figma.getLocalPaintStylesAsync();
97
+ if (styles.length > 0) {
98
+ result.paintStyles = styles.map((s) => ({
99
+ id: s.id,
100
+ name: s.name,
101
+ paints: s.paints.map(serializePaint)
102
+ }));
103
+ }
104
+ }
105
+ if (!type || type === "all" || type === "text") {
106
+ const styles = yield figma.getLocalTextStylesAsync();
107
+ if (styles.length > 0) {
108
+ result.textStyles = styles.map((s) => ({
109
+ id: s.id,
110
+ name: s.name,
111
+ fontSize: s.fontSize,
112
+ fontFamily: s.fontName.family,
113
+ fontStyle: s.fontName.style
114
+ }));
115
+ }
116
+ }
117
+ if (!type || type === "all" || type === "effect") {
118
+ const styles = yield figma.getLocalEffectStylesAsync();
119
+ if (styles.length > 0) {
120
+ result.effectStyles = styles.map((s) => ({
121
+ id: s.id,
122
+ name: s.name,
123
+ effects: s.effects.map((e) => ({ type: e.type, radius: e.radius }))
124
+ }));
125
+ }
126
+ }
127
+ if (!type || type === "all" || type === "grid") {
128
+ const styles = yield figma.getLocalGridStylesAsync();
129
+ if (styles.length > 0) {
130
+ result.gridStyles = styles.map((s) => ({
131
+ id: s.id,
132
+ name: s.name,
133
+ grids: s.layoutGrids.length
134
+ }));
135
+ }
136
+ }
137
+ return result;
138
+ }
139
+ case "get-viewport":
140
+ return {
141
+ center: figma.viewport.center,
142
+ zoom: figma.viewport.zoom,
143
+ bounds: figma.viewport.bounds
144
+ };
145
+ // ==================== CREATE SHAPES ====================
146
+ case "create-rectangle": {
147
+ const { x, y, width, height, name, parentId, fill, stroke, strokeWeight, radius, opacity } = args;
148
+ const rect = figma.createRectangle();
149
+ rect.x = x;
150
+ rect.y = y;
151
+ rect.resize(width, height);
152
+ if (name) rect.name = name;
153
+ if (fill) rect.fills = [{ type: "SOLID", color: hexToRgb(fill) }];
154
+ if (stroke) rect.strokes = [{ type: "SOLID", color: hexToRgb(stroke) }];
155
+ if (strokeWeight !== void 0) rect.strokeWeight = strokeWeight;
156
+ if (radius !== void 0) rect.cornerRadius = radius;
157
+ if (opacity !== void 0) rect.opacity = opacity;
158
+ yield appendToParent(rect, parentId);
159
+ return serializeNode(rect);
160
+ }
161
+ case "create-ellipse": {
162
+ const { x, y, width, height, name, parentId, fill, stroke, strokeWeight, opacity } = args;
163
+ const ellipse = figma.createEllipse();
164
+ ellipse.x = x;
165
+ ellipse.y = y;
166
+ ellipse.resize(width, height);
167
+ if (name) ellipse.name = name;
168
+ if (fill) ellipse.fills = [{ type: "SOLID", color: hexToRgb(fill) }];
169
+ if (stroke) ellipse.strokes = [{ type: "SOLID", color: hexToRgb(stroke) }];
170
+ if (strokeWeight !== void 0) ellipse.strokeWeight = strokeWeight;
171
+ if (opacity !== void 0) ellipse.opacity = opacity;
172
+ yield appendToParent(ellipse, parentId);
173
+ return serializeNode(ellipse);
174
+ }
175
+ case "create-line": {
176
+ const { x, y, length, rotation, name, parentId } = args;
177
+ const line = figma.createLine();
178
+ line.x = x;
179
+ line.y = y;
180
+ line.resize(length, 0);
181
+ if (rotation) line.rotation = rotation;
182
+ if (name) line.name = name;
183
+ yield appendToParent(line, parentId);
184
+ return serializeNode(line);
185
+ }
186
+ case "create-polygon": {
187
+ const { x, y, size, sides, name, parentId } = args;
188
+ const polygon = figma.createPolygon();
189
+ polygon.x = x;
190
+ polygon.y = y;
191
+ polygon.resize(size, size);
192
+ if (sides) polygon.pointCount = sides;
193
+ if (name) polygon.name = name;
194
+ yield appendToParent(polygon, parentId);
195
+ return serializeNode(polygon);
196
+ }
197
+ case "create-star": {
198
+ const { x, y, size, points, innerRadius, name, parentId } = args;
199
+ const star = figma.createStar();
200
+ star.x = x;
201
+ star.y = y;
202
+ star.resize(size, size);
203
+ if (points) star.pointCount = points;
204
+ if (innerRadius !== void 0) star.innerRadius = innerRadius;
205
+ if (name) star.name = name;
206
+ yield appendToParent(star, parentId);
207
+ return serializeNode(star);
208
+ }
209
+ case "create-vector": {
210
+ const { x, y, path, name, parentId } = args;
211
+ const frame = figma.createNodeFromSvg(`<svg><path d="${path}"/></svg>`);
212
+ frame.x = x;
213
+ frame.y = y;
214
+ if (name) frame.name = name;
215
+ yield appendToParent(frame, parentId);
216
+ return serializeNode(frame);
217
+ }
218
+ // ==================== CREATE CONTAINERS ====================
219
+ case "create-frame": {
220
+ const { x, y, width, height, name, parentId, fill, stroke, strokeWeight, radius, opacity, layoutMode, itemSpacing, padding } = args;
221
+ const frame = figma.createFrame();
222
+ frame.x = x;
223
+ frame.y = y;
224
+ frame.resize(width, height);
225
+ if (name) frame.name = name;
226
+ if (fill) frame.fills = [{ type: "SOLID", color: hexToRgb(fill) }];
227
+ if (stroke) frame.strokes = [{ type: "SOLID", color: hexToRgb(stroke) }];
228
+ if (strokeWeight !== void 0) frame.strokeWeight = strokeWeight;
229
+ if (radius !== void 0) frame.cornerRadius = radius;
230
+ if (opacity !== void 0) frame.opacity = opacity;
231
+ if (layoutMode && layoutMode !== "NONE") {
232
+ frame.layoutMode = layoutMode;
233
+ if (itemSpacing !== void 0) frame.itemSpacing = itemSpacing;
234
+ if (padding) {
235
+ frame.paddingTop = padding.top;
236
+ frame.paddingRight = padding.right;
237
+ frame.paddingBottom = padding.bottom;
238
+ frame.paddingLeft = padding.left;
239
+ }
240
+ }
241
+ yield appendToParent(frame, parentId);
242
+ return serializeNode(frame);
243
+ }
244
+ case "create-section": {
245
+ const { x, y, width, height, name } = args;
246
+ const section = figma.createSection();
247
+ section.x = x;
248
+ section.y = y;
249
+ section.resizeWithoutConstraints(width, height);
250
+ if (name) section.name = name;
251
+ return serializeNode(section);
252
+ }
253
+ case "create-slice": {
254
+ const { x, y, width, height, name } = args;
255
+ const slice = figma.createSlice();
256
+ slice.x = x;
257
+ slice.y = y;
258
+ slice.resize(width, height);
259
+ if (name) slice.name = name;
260
+ return serializeNode(slice);
261
+ }
262
+ // ==================== CREATE OTHER ====================
263
+ case "create-text": {
264
+ const { x, y, text, fontSize, fontFamily, fontStyle, fill, opacity, name, parentId } = args;
265
+ const textNode = figma.createText();
266
+ const family = fontFamily || "Inter";
267
+ const style = fontStyle || "Regular";
268
+ yield figma.loadFontAsync({ family, style });
269
+ textNode.x = x;
270
+ textNode.y = y;
271
+ textNode.fontName = { family, style };
272
+ textNode.characters = text;
273
+ if (fontSize) textNode.fontSize = fontSize;
274
+ if (fill) textNode.fills = [{ type: "SOLID", color: hexToRgb(fill) }];
275
+ if (opacity !== void 0) textNode.opacity = opacity;
276
+ if (name) textNode.name = name;
277
+ yield appendToParent(textNode, parentId);
278
+ return serializeNode(textNode);
279
+ }
280
+ case "create-instance": {
281
+ const { componentId, x, y, name, parentId } = args;
282
+ const component = yield figma.getNodeByIdAsync(componentId);
283
+ if (!component || component.type !== "COMPONENT") throw new Error("Component not found");
284
+ const instance = component.createInstance();
285
+ if (x !== void 0) instance.x = x;
286
+ if (y !== void 0) instance.y = y;
287
+ if (name) instance.name = name;
288
+ yield appendToParent(instance, parentId);
289
+ return serializeNode(instance);
290
+ }
291
+ case "create-component": {
292
+ const { name, parentId } = args;
293
+ const component = figma.createComponent();
294
+ component.name = name;
295
+ yield appendToParent(component, parentId);
296
+ return serializeNode(component);
297
+ }
298
+ case "clone-node": {
299
+ const { id } = args;
300
+ const node = yield figma.getNodeByIdAsync(id);
301
+ if (!node || !("clone" in node)) throw new Error("Node not found");
302
+ const clone = node.clone();
303
+ return serializeNode(clone);
304
+ }
305
+ // ==================== CREATE STYLES ====================
306
+ case "create-paint-style": {
307
+ const { name, color } = args;
308
+ const style = figma.createPaintStyle();
309
+ style.name = name;
310
+ style.paints = [{ type: "SOLID", color: hexToRgb(color) }];
311
+ return { id: style.id, name: style.name, key: style.key };
312
+ }
313
+ case "create-text-style": {
314
+ const { name, fontFamily, fontStyle, fontSize } = args;
315
+ const style = figma.createTextStyle();
316
+ style.name = name;
317
+ yield figma.loadFontAsync({ family: fontFamily || "Inter", style: fontStyle || "Regular" });
318
+ style.fontName = { family: fontFamily || "Inter", style: fontStyle || "Regular" };
319
+ if (fontSize) style.fontSize = fontSize;
320
+ return { id: style.id, name: style.name, key: style.key };
321
+ }
322
+ case "create-effect-style": {
323
+ const { name, type, radius, color, offsetX, offsetY } = args;
324
+ const style = figma.createEffectStyle();
325
+ style.name = name;
326
+ const rgba = color ? hexToRgba(color) : { r: 0, g: 0, b: 0, a: 0.25 };
327
+ if (type === "DROP_SHADOW" || type === "INNER_SHADOW") {
328
+ style.effects = [{
329
+ type,
330
+ color: rgba,
331
+ offset: { x: offsetX || 0, y: offsetY || 4 },
332
+ radius: radius || 10,
333
+ spread: 0,
334
+ visible: true,
335
+ blendMode: "NORMAL"
336
+ }];
337
+ } else if (type === "BLUR" || type === "BACKGROUND_BLUR") {
338
+ style.effects = [{
339
+ type,
340
+ radius: radius || 10,
341
+ visible: true
342
+ }];
343
+ }
344
+ return { id: style.id, name: style.name, key: style.key };
345
+ }
346
+ // ==================== UPDATE POSITION/SIZE ====================
347
+ case "move-node": {
348
+ const { id, x, y } = args;
349
+ const node = yield figma.getNodeByIdAsync(id);
350
+ if (!node) throw new Error("Node not found");
351
+ node.x = x;
352
+ node.y = y;
353
+ return serializeNode(node);
354
+ }
355
+ case "resize-node": {
356
+ const { id, width, height } = args;
357
+ const node = yield figma.getNodeByIdAsync(id);
358
+ if (!node || !("resize" in node)) throw new Error("Node not found");
359
+ node.resize(width, height);
360
+ return serializeNode(node);
361
+ }
362
+ // ==================== UPDATE APPEARANCE ====================
363
+ case "set-fill-color": {
364
+ const { id, color } = args;
365
+ const node = yield figma.getNodeByIdAsync(id);
366
+ if (!node || !("fills" in node)) throw new Error("Node not found");
367
+ node.fills = [{ type: "SOLID", color: hexToRgb(color) }];
368
+ return serializeNode(node);
369
+ }
370
+ case "set-stroke-color": {
371
+ const { id, color } = args;
372
+ const node = yield figma.getNodeByIdAsync(id);
373
+ if (!node || !("strokes" in node)) throw new Error("Node not found");
374
+ node.strokes = [{ type: "SOLID", color: hexToRgb(color) }];
375
+ return serializeNode(node);
376
+ }
377
+ case "set-corner-radius": {
378
+ const { id, cornerRadius, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius } = args;
379
+ const node = yield figma.getNodeByIdAsync(id);
380
+ if (!node || !("cornerRadius" in node)) throw new Error("Node not found");
381
+ node.cornerRadius = cornerRadius;
382
+ if (topLeftRadius !== void 0) node.topLeftRadius = topLeftRadius;
383
+ if (topRightRadius !== void 0) node.topRightRadius = topRightRadius;
384
+ if (bottomLeftRadius !== void 0) node.bottomLeftRadius = bottomLeftRadius;
385
+ if (bottomRightRadius !== void 0) node.bottomRightRadius = bottomRightRadius;
386
+ return serializeNode(node);
387
+ }
388
+ case "set-opacity": {
389
+ const { id, opacity } = args;
390
+ const node = yield figma.getNodeByIdAsync(id);
391
+ if (!node || !("opacity" in node)) throw new Error("Node not found");
392
+ node.opacity = opacity;
393
+ return serializeNode(node);
394
+ }
395
+ case "set-image-fill": {
396
+ const { id, url, scaleMode } = args;
397
+ const node = yield figma.getNodeByIdAsync(id);
398
+ if (!node || !("fills" in node)) throw new Error("Node not found");
399
+ const image = yield figma.createImageAsync(url);
400
+ node.fills = [{
401
+ type: "IMAGE",
402
+ imageHash: image.hash,
403
+ scaleMode: scaleMode || "FILL"
404
+ }];
405
+ return serializeNode(node);
406
+ }
407
+ // ==================== UPDATE PROPERTIES ====================
408
+ case "rename-node": {
409
+ const { id, name } = args;
410
+ const node = yield figma.getNodeByIdAsync(id);
411
+ if (!node) throw new Error("Node not found");
412
+ node.name = name;
413
+ return serializeNode(node);
414
+ }
415
+ case "set-visible": {
416
+ const { id, visible } = args;
417
+ const node = yield figma.getNodeByIdAsync(id);
418
+ if (!node) throw new Error("Node not found");
419
+ node.visible = visible;
420
+ return serializeNode(node);
421
+ }
422
+ case "set-locked": {
423
+ const { id, locked } = args;
424
+ const node = yield figma.getNodeByIdAsync(id);
425
+ if (!node) throw new Error("Node not found");
426
+ node.locked = locked;
427
+ return serializeNode(node);
428
+ }
429
+ case "set-effect": {
430
+ const { id, type, color, offsetX, offsetY, radius, spread } = args;
431
+ const node = yield figma.getNodeByIdAsync(id);
432
+ if (!node || !("effects" in node)) throw new Error("Node not found");
433
+ const rgba = color ? hexToRgba(color) : { r: 0, g: 0, b: 0, a: 0.25 };
434
+ if (type === "DROP_SHADOW" || type === "INNER_SHADOW") {
435
+ node.effects = [{
436
+ type,
437
+ color: rgba,
438
+ offset: { x: offsetX != null ? offsetX : 0, y: offsetY != null ? offsetY : 4 },
439
+ radius: radius != null ? radius : 8,
440
+ spread: spread != null ? spread : 0,
441
+ visible: true,
442
+ blendMode: "NORMAL"
443
+ }];
444
+ } else if (type === "BLUR") {
445
+ node.effects = [{
446
+ type: "LAYER_BLUR",
447
+ radius: radius != null ? radius : 8,
448
+ visible: true
449
+ }];
450
+ }
451
+ return serializeNode(node);
452
+ }
453
+ case "set-text": {
454
+ const { id, text } = args;
455
+ const node = yield figma.getNodeByIdAsync(id);
456
+ if (!node || node.type !== "TEXT") throw new Error("Text node not found");
457
+ const fontName = node.fontName;
458
+ yield figma.loadFontAsync(fontName);
459
+ node.characters = text;
460
+ return serializeNode(node);
461
+ }
462
+ case "import-svg": {
463
+ const { svg, x, y, name, parentId } = args;
464
+ const node = figma.createNodeFromSvg(svg);
465
+ if (x !== void 0) node.x = x;
466
+ if (y !== void 0) node.y = y;
467
+ if (name) node.name = name;
468
+ yield appendToParent(node, parentId);
469
+ return serializeNode(node);
470
+ }
471
+ case "set-font": {
472
+ const { id, fontFamily, fontStyle, fontSize } = args;
473
+ const node = yield figma.getNodeByIdAsync(id);
474
+ if (!node || node.type !== "TEXT") throw new Error("Text node not found");
475
+ const currentFont = node.fontName;
476
+ const family = fontFamily || currentFont.family;
477
+ const style = fontStyle || currentFont.style;
478
+ yield figma.loadFontAsync({ family, style });
479
+ node.fontName = { family, style };
480
+ if (fontSize !== void 0) node.fontSize = fontSize;
481
+ return serializeNode(node);
482
+ }
483
+ case "get-children": {
484
+ const { id, depth } = args;
485
+ const node = yield figma.getNodeByIdAsync(id);
486
+ if (!node) throw new Error("Node not found");
487
+ if (!("children" in node)) return [];
488
+ const maxDepth = depth || 1;
489
+ const serializeWithDepth = (n, d) => {
490
+ const base = serializeNode(n);
491
+ if (d < maxDepth && "children" in n) {
492
+ base.children = n.children.map((c) => serializeWithDepth(c, d + 1));
493
+ }
494
+ return base;
495
+ };
496
+ return node.children.map((c) => serializeWithDepth(c, 1));
497
+ }
498
+ case "find-by-name": {
499
+ const { name, type, exact } = args;
500
+ const results = [];
501
+ figma.currentPage.findAll((n) => {
502
+ const nameMatch = exact ? n.name === name : n.name.toLowerCase().includes(name.toLowerCase());
503
+ const typeMatch = !type || n.type === type;
504
+ if (nameMatch && typeMatch) results.push(serializeNode(n));
505
+ return false;
506
+ });
507
+ return results;
508
+ }
509
+ case "select-nodes": {
510
+ const { ids } = args;
511
+ const nodes = yield Promise.all(ids.map((id) => figma.getNodeByIdAsync(id)));
512
+ const validNodes = nodes.filter((n) => n !== null && "id" in n);
513
+ figma.currentPage.selection = validNodes;
514
+ return { selected: validNodes.length };
515
+ }
516
+ case "set-constraints": {
517
+ const { id, horizontal, vertical } = args;
518
+ const node = yield figma.getNodeByIdAsync(id);
519
+ if (!node || !("constraints" in node)) throw new Error("Node not found");
520
+ node.constraints = {
521
+ horizontal: horizontal || node.constraints.horizontal,
522
+ vertical: vertical || node.constraints.vertical
523
+ };
524
+ return serializeNode(node);
525
+ }
526
+ case "set-blend-mode": {
527
+ const { id, mode } = args;
528
+ const node = yield figma.getNodeByIdAsync(id);
529
+ if (!node || !("blendMode" in node)) throw new Error("Node not found");
530
+ node.blendMode = mode;
531
+ return serializeNode(node);
532
+ }
533
+ case "set-auto-layout": {
534
+ const { id, mode, wrap, itemSpacing, counterSpacing, padding, primaryAlign, counterAlign, sizingH, sizingV } = args;
535
+ const node = yield figma.getNodeByIdAsync(id);
536
+ if (!node || !("layoutMode" in node)) throw new Error("Frame not found");
537
+ if (mode) node.layoutMode = mode;
538
+ if (wrap !== void 0) node.layoutWrap = wrap ? "WRAP" : "NO_WRAP";
539
+ if (itemSpacing !== void 0) node.itemSpacing = itemSpacing;
540
+ if (counterSpacing !== void 0) node.counterAxisSpacing = counterSpacing;
541
+ if (padding) {
542
+ node.paddingTop = padding.top;
543
+ node.paddingRight = padding.right;
544
+ node.paddingBottom = padding.bottom;
545
+ node.paddingLeft = padding.left;
546
+ }
547
+ if (primaryAlign) node.primaryAxisAlignItems = primaryAlign;
548
+ if (counterAlign) node.counterAxisAlignItems = counterAlign;
549
+ if (sizingH) node.layoutSizingHorizontal = sizingH;
550
+ if (sizingV) node.layoutSizingVertical = sizingV;
551
+ return serializeNode(node);
552
+ }
553
+ case "set-layout-child": {
554
+ const { id, horizontalSizing, verticalSizing, positioning, x, y } = args;
555
+ const node = yield figma.getNodeByIdAsync(id);
556
+ if (!node) throw new Error("Node not found");
557
+ if (horizontalSizing && "layoutSizingHorizontal" in node) {
558
+ node.layoutSizingHorizontal = horizontalSizing;
559
+ }
560
+ if (verticalSizing && "layoutSizingVertical" in node) {
561
+ node.layoutSizingVertical = verticalSizing;
562
+ }
563
+ if (positioning && "layoutPositioning" in node) {
564
+ node.layoutPositioning = positioning;
565
+ }
566
+ if (positioning === "ABSOLUTE") {
567
+ if (x !== void 0) node.x = x;
568
+ if (y !== void 0) node.y = y;
569
+ }
570
+ return serializeNode(node);
571
+ }
572
+ case "set-text-properties": {
573
+ const { id, lineHeight, letterSpacing, textAlign, verticalAlign, autoResize, maxLines, paragraphSpacing, paragraphIndent } = args;
574
+ const node = yield figma.getNodeByIdAsync(id);
575
+ if (!node || node.type !== "TEXT") throw new Error("Text node not found");
576
+ const fontName = node.fontName;
577
+ yield figma.loadFontAsync(fontName);
578
+ if (lineHeight !== void 0) {
579
+ node.lineHeight = lineHeight === "auto" ? { unit: "AUTO" } : { unit: "PIXELS", value: lineHeight };
580
+ }
581
+ if (letterSpacing !== void 0) {
582
+ node.letterSpacing = { unit: "PIXELS", value: letterSpacing };
583
+ }
584
+ if (textAlign) node.textAlignHorizontal = textAlign;
585
+ if (verticalAlign) node.textAlignVertical = verticalAlign;
586
+ if (autoResize) node.textAutoResize = autoResize;
587
+ if (maxLines !== void 0) node.maxLines = maxLines;
588
+ if (paragraphSpacing !== void 0) node.paragraphSpacing = paragraphSpacing;
589
+ if (paragraphIndent !== void 0) node.paragraphIndent = paragraphIndent;
590
+ return serializeNode(node);
591
+ }
592
+ case "set-min-max": {
593
+ const { id, minWidth, maxWidth, minHeight, maxHeight } = args;
594
+ const node = yield figma.getNodeByIdAsync(id);
595
+ if (!node) throw new Error("Node not found");
596
+ if (minWidth !== void 0 && "minWidth" in node) node.minWidth = minWidth;
597
+ if (maxWidth !== void 0 && "maxWidth" in node) node.maxWidth = maxWidth;
598
+ if (minHeight !== void 0 && "minHeight" in node) node.minHeight = minHeight;
599
+ if (maxHeight !== void 0 && "maxHeight" in node) node.maxHeight = maxHeight;
600
+ return serializeNode(node);
601
+ }
602
+ case "set-rotation": {
603
+ const { id, angle } = args;
604
+ const node = yield figma.getNodeByIdAsync(id);
605
+ if (!node) throw new Error("Node not found");
606
+ node.rotation = angle;
607
+ return serializeNode(node);
608
+ }
609
+ case "set-stroke-align": {
610
+ const { id, align } = args;
611
+ const node = yield figma.getNodeByIdAsync(id);
612
+ if (!node || !("strokeAlign" in node)) throw new Error("Node not found");
613
+ node.strokeAlign = align;
614
+ return serializeNode(node);
615
+ }
616
+ // ==================== UPDATE STRUCTURE ====================
617
+ case "set-layout": {
618
+ const {
619
+ id,
620
+ mode,
621
+ wrap,
622
+ clip,
623
+ itemSpacing,
624
+ primaryAxisAlignItems,
625
+ counterAxisAlignItems,
626
+ paddingLeft,
627
+ paddingRight,
628
+ paddingTop,
629
+ paddingBottom,
630
+ layoutSizingVertical,
631
+ layoutSizingHorizontal
632
+ } = args;
633
+ const node = yield figma.getNodeByIdAsync(id);
634
+ if (!node || !("layoutMode" in node)) throw new Error("Node not found");
635
+ node.layoutMode = mode;
636
+ if (wrap !== void 0) node.layoutWrap = wrap ? "WRAP" : "NO_WRAP";
637
+ if (clip !== void 0) node.clipsContent = clip;
638
+ if (itemSpacing !== void 0) node.itemSpacing = itemSpacing;
639
+ if (primaryAxisAlignItems) node.primaryAxisAlignItems = primaryAxisAlignItems;
640
+ if (counterAxisAlignItems) node.counterAxisAlignItems = counterAxisAlignItems;
641
+ if (paddingLeft !== void 0) node.paddingLeft = paddingLeft;
642
+ if (paddingRight !== void 0) node.paddingRight = paddingRight;
643
+ if (paddingTop !== void 0) node.paddingTop = paddingTop;
644
+ if (paddingBottom !== void 0) node.paddingBottom = paddingBottom;
645
+ if (layoutSizingVertical) node.layoutSizingVertical = layoutSizingVertical;
646
+ if (layoutSizingHorizontal) node.layoutSizingHorizontal = layoutSizingHorizontal;
647
+ return serializeNode(node);
648
+ }
649
+ case "set-parent-id": {
650
+ const { id, parentId } = args;
651
+ const node = yield figma.getNodeByIdAsync(id);
652
+ const parent = yield figma.getNodeByIdAsync(parentId);
653
+ if (!node || !parent) throw new Error("Node or parent not found");
654
+ parent.appendChild(node);
655
+ return serializeNode(node);
656
+ }
657
+ case "group-nodes": {
658
+ const { ids, name } = args;
659
+ const nodes = yield Promise.all(ids.map((id) => figma.getNodeByIdAsync(id)));
660
+ const validNodes = nodes.filter((n) => n !== null && "parent" in n);
661
+ if (validNodes.length === 0) throw new Error("No valid nodes found");
662
+ const parent = validNodes[0].parent;
663
+ if (!parent || !("children" in parent)) throw new Error("Invalid parent");
664
+ const group = figma.group(validNodes, parent);
665
+ if (name) group.name = name;
666
+ return serializeNode(group);
667
+ }
668
+ case "ungroup-node": {
669
+ const { id } = args;
670
+ const node = yield figma.getNodeByIdAsync(id);
671
+ if (!node || node.type !== "GROUP") throw new Error("Not a group node");
672
+ const children = figma.ungroup(node);
673
+ return children.map(serializeNode);
674
+ }
675
+ case "flatten-nodes": {
676
+ const { ids } = args;
677
+ const nodes = yield Promise.all(ids.map((id) => figma.getNodeByIdAsync(id)));
678
+ const validNodes = nodes.filter((n) => n !== null && "parent" in n);
679
+ if (validNodes.length === 0) throw new Error("No valid nodes found");
680
+ const vector = figma.flatten(validNodes);
681
+ return serializeNode(vector);
682
+ }
683
+ // ==================== BOOLEAN OPERATIONS ====================
684
+ case "boolean-operation": {
685
+ const { ids, operation } = args;
686
+ const nodes = yield Promise.all(ids.map((id) => figma.getNodeByIdAsync(id)));
687
+ const validNodes = nodes.filter((n) => n !== null && "parent" in n);
688
+ if (validNodes.length < 2) throw new Error("Need at least 2 nodes");
689
+ const parent = validNodes[0].parent;
690
+ if (!parent || !("children" in parent)) throw new Error("Invalid parent");
691
+ let result;
692
+ switch (operation) {
693
+ case "UNION":
694
+ result = figma.union(validNodes, parent);
695
+ break;
696
+ case "SUBTRACT":
697
+ result = figma.subtract(validNodes, parent);
698
+ break;
699
+ case "INTERSECT":
700
+ result = figma.intersect(validNodes, parent);
701
+ break;
702
+ case "EXCLUDE":
703
+ result = figma.exclude(validNodes, parent);
704
+ break;
705
+ }
706
+ return serializeNode(result);
707
+ }
708
+ // ==================== INSTANCE/COMPONENT ====================
709
+ case "set-instance-properties": {
710
+ const { instanceId, properties } = args;
711
+ const instance = yield figma.getNodeByIdAsync(instanceId);
712
+ if (!instance || instance.type !== "INSTANCE") throw new Error("Instance not found");
713
+ instance.setProperties(properties);
714
+ return serializeNode(instance);
715
+ }
716
+ case "set-node-component-property-references": {
717
+ const { id, componentPropertyReferences } = args;
718
+ const node = yield figma.getNodeByIdAsync(id);
719
+ if (!node || !("componentPropertyReferences" in node)) throw new Error("Node not found");
720
+ for (const [key, value] of Object.entries(componentPropertyReferences)) {
721
+ node.componentPropertyReferences = __spreadProps(__spreadValues({}, node.componentPropertyReferences), { [key]: value });
722
+ }
723
+ return serializeNode(node);
724
+ }
725
+ case "add-component-property": {
726
+ const { componentId, name, type, defaultValue } = args;
727
+ const component = yield figma.getNodeByIdAsync(componentId);
728
+ if (!component || component.type !== "COMPONENT" && component.type !== "COMPONENT_SET") {
729
+ throw new Error("Component not found");
730
+ }
731
+ let parsedDefault = defaultValue;
732
+ if (type === "BOOLEAN") parsedDefault = defaultValue === "true" || defaultValue === true;
733
+ component.addComponentProperty(name, type, parsedDefault);
734
+ return serializeNode(component);
735
+ }
736
+ case "edit-component-property": {
737
+ const { componentId, name, defaultValue, preferredValues } = args;
738
+ const component = yield figma.getNodeByIdAsync(componentId);
739
+ if (!component || component.type !== "COMPONENT" && component.type !== "COMPONENT_SET") {
740
+ throw new Error("Component not found");
741
+ }
742
+ const props = component.componentPropertyDefinitions;
743
+ const propKey = Object.keys(props).find((k) => k === name || k.startsWith(name + "#"));
744
+ if (!propKey) throw new Error("Property not found");
745
+ const propDef = props[propKey];
746
+ let parsedDefault = defaultValue;
747
+ if (propDef.type === "BOOLEAN") parsedDefault = defaultValue === "true" || defaultValue === true;
748
+ component.editComponentProperty(propKey, {
749
+ name,
750
+ defaultValue: parsedDefault,
751
+ preferredValues: preferredValues == null ? void 0 : preferredValues.map((v) => ({ type: "COMPONENT", key: v }))
752
+ });
753
+ return serializeNode(component);
754
+ }
755
+ case "delete-component-property": {
756
+ const { componentId, name } = args;
757
+ const component = yield figma.getNodeByIdAsync(componentId);
758
+ if (!component || component.type !== "COMPONENT" && component.type !== "COMPONENT_SET") {
759
+ throw new Error("Component not found");
760
+ }
761
+ const props = component.componentPropertyDefinitions;
762
+ const propKey = Object.keys(props).find((k) => k === name || k.startsWith(name + "#"));
763
+ if (!propKey) throw new Error("Property not found");
764
+ component.deleteComponentProperty(propKey);
765
+ return serializeNode(component);
766
+ }
767
+ // ==================== VIEWPORT ====================
768
+ case "set-viewport": {
769
+ const { x, y, zoom } = args;
770
+ if (x !== void 0 && y !== void 0) {
771
+ figma.viewport.center = { x, y };
772
+ }
773
+ if (zoom !== void 0) {
774
+ figma.viewport.zoom = zoom;
775
+ }
776
+ return { center: figma.viewport.center, zoom: figma.viewport.zoom };
777
+ }
778
+ case "zoom-to-fit": {
779
+ const { ids } = args;
780
+ let nodes;
781
+ if (ids && ids.length > 0) {
782
+ const fetched = yield Promise.all(ids.map((id) => figma.getNodeByIdAsync(id)));
783
+ nodes = fetched.filter((n) => n !== null && "absoluteBoundingBox" in n);
784
+ } else {
785
+ nodes = figma.currentPage.selection;
786
+ }
787
+ if (nodes.length > 0) {
788
+ figma.viewport.scrollAndZoomIntoView(nodes);
789
+ }
790
+ return { center: figma.viewport.center, zoom: figma.viewport.zoom };
791
+ }
792
+ // ==================== EXPORT ====================
793
+ case "export-node": {
794
+ const { id, format, scale } = args;
795
+ const node = yield figma.getNodeByIdAsync(id);
796
+ if (!node) throw new Error("Node not found");
797
+ const bytes = yield node.exportAsync(__spreadValues({
798
+ format
799
+ }, format !== "SVG" && format !== "PDF" ? { scale: scale || 1 } : {}));
800
+ return {
801
+ data: figma.base64Encode(bytes),
802
+ filename: `${node.name}.${format.toLowerCase()}`
803
+ };
804
+ }
805
+ case "screenshot": {
806
+ const { scale } = args;
807
+ const bounds = figma.viewport.bounds;
808
+ const frame = figma.createFrame();
809
+ frame.name = "__screenshot_temp__";
810
+ frame.x = bounds.x;
811
+ frame.y = bounds.y;
812
+ frame.resize(bounds.width, bounds.height);
813
+ frame.fills = [];
814
+ frame.clipsContent = true;
815
+ for (const node of figma.currentPage.children) {
816
+ if (node.id === frame.id) continue;
817
+ if (!node.visible) continue;
818
+ if ("absoluteBoundingBox" in node && node.absoluteBoundingBox) {
819
+ const nb = node.absoluteBoundingBox;
820
+ if (nb.x + nb.width > bounds.x && nb.x < bounds.x + bounds.width && nb.y + nb.height > bounds.y && nb.y < bounds.y + bounds.height) {
821
+ const clone = node.clone();
822
+ clone.x = node.x - bounds.x;
823
+ clone.y = node.y - bounds.y;
824
+ frame.appendChild(clone);
825
+ }
826
+ }
827
+ }
828
+ const bytes = yield frame.exportAsync({ format: "PNG", scale: scale || 1 });
829
+ frame.remove();
830
+ return { data: figma.base64Encode(bytes) };
831
+ }
832
+ case "export-selection": {
833
+ const { format, scale, padding } = args;
834
+ const selection = figma.currentPage.selection;
835
+ if (selection.length === 0) throw new Error("No selection");
836
+ if (selection.length === 1 && !padding) {
837
+ const bytes2 = yield selection[0].exportAsync(__spreadValues({
838
+ format: format || "PNG"
839
+ }, format !== "SVG" && format !== "PDF" ? { scale: scale || 2 } : {}));
840
+ return { data: figma.base64Encode(bytes2) };
841
+ }
842
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
843
+ for (const node of selection) {
844
+ if ("absoluteBoundingBox" in node && node.absoluteBoundingBox) {
845
+ const b = node.absoluteBoundingBox;
846
+ minX = Math.min(minX, b.x);
847
+ minY = Math.min(minY, b.y);
848
+ maxX = Math.max(maxX, b.x + b.width);
849
+ maxY = Math.max(maxY, b.y + b.height);
850
+ }
851
+ }
852
+ const pad = padding || 0;
853
+ const frame = figma.createFrame();
854
+ frame.name = "__export_temp__";
855
+ frame.x = minX - pad;
856
+ frame.y = minY - pad;
857
+ frame.resize(maxX - minX + pad * 2, maxY - minY + pad * 2);
858
+ frame.fills = [{ type: "SOLID", color: { r: 1, g: 1, b: 1 } }];
859
+ frame.clipsContent = true;
860
+ for (const node of selection) {
861
+ const clone = node.clone();
862
+ clone.x = node.x - frame.x;
863
+ clone.y = node.y - frame.y;
864
+ frame.appendChild(clone);
865
+ }
866
+ const bytes = yield frame.exportAsync(__spreadValues({
867
+ format: format || "PNG"
868
+ }, format !== "SVG" && format !== "PDF" ? { scale: scale || 2 } : {}));
869
+ frame.remove();
870
+ return { data: figma.base64Encode(bytes) };
871
+ }
872
+ // ==================== DELETE ====================
873
+ case "delete-node": {
874
+ const { id } = args;
875
+ const node = yield figma.getNodeByIdAsync(id);
876
+ if (!node || !("remove" in node)) throw new Error("Node not found");
877
+ node.remove();
878
+ return { deleted: true };
879
+ }
880
+ default:
881
+ throw new Error(`Unknown command: ${command}`);
882
+ }
883
+ });
884
+ }
885
+ function appendToParent(node, parentId) {
886
+ return __async(this, null, function* () {
887
+ if (parentId) {
888
+ const parent = yield figma.getNodeByIdAsync(parentId);
889
+ if (parent && "appendChild" in parent) {
890
+ parent.appendChild(node);
891
+ return;
892
+ }
893
+ }
894
+ figma.currentPage.appendChild(node);
895
+ });
896
+ }
897
+ function serializeNode(node) {
898
+ const base = {
899
+ id: node.id,
900
+ name: node.name,
901
+ type: node.type
902
+ };
903
+ if ("x" in node) base.x = Math.round(node.x);
904
+ if ("y" in node) base.y = Math.round(node.y);
905
+ if ("width" in node) base.width = Math.round(node.width);
906
+ if ("height" in node) base.height = Math.round(node.height);
907
+ if ("opacity" in node && node.opacity !== 1) base.opacity = node.opacity;
908
+ if ("visible" in node && !node.visible) base.visible = false;
909
+ if ("locked" in node && node.locked) base.locked = true;
910
+ if ("fills" in node && Array.isArray(node.fills) && node.fills.length > 0) {
911
+ base.fills = node.fills.map(serializePaint);
912
+ }
913
+ if ("strokes" in node && Array.isArray(node.strokes) && node.strokes.length > 0) {
914
+ base.strokes = node.strokes.map(serializePaint);
915
+ }
916
+ if ("strokeWeight" in node && typeof node.strokeWeight === "number" && node.strokeWeight > 0) {
917
+ base.strokeWeight = node.strokeWeight;
918
+ }
919
+ if ("componentPropertyDefinitions" in node) {
920
+ base.componentPropertyDefinitions = node.componentPropertyDefinitions;
921
+ }
922
+ if ("componentProperties" in node) {
923
+ base.componentProperties = node.componentProperties;
924
+ }
925
+ if ("layoutMode" in node && node.layoutMode !== "NONE") {
926
+ base.layoutMode = node.layoutMode;
927
+ if ("itemSpacing" in node) base.itemSpacing = node.itemSpacing;
928
+ if ("paddingLeft" in node && (node.paddingLeft || node.paddingRight || node.paddingTop || node.paddingBottom)) {
929
+ base.padding = { left: node.paddingLeft, right: node.paddingRight, top: node.paddingTop, bottom: node.paddingBottom };
930
+ }
931
+ }
932
+ if (node.type === "TEXT") {
933
+ const textNode = node;
934
+ base.characters = textNode.characters;
935
+ if (typeof textNode.fontSize === "number") base.fontSize = textNode.fontSize;
936
+ }
937
+ if ("children" in node) {
938
+ base.childCount = node.children.length;
939
+ }
940
+ return base;
941
+ }
942
+ function serializePaint(paint) {
943
+ if (paint.type === "SOLID") {
944
+ const result = {
945
+ type: "SOLID",
946
+ color: rgbToHex(paint.color)
947
+ };
948
+ if (paint.opacity !== void 0 && paint.opacity !== 1) result.opacity = paint.opacity;
949
+ return result;
950
+ }
951
+ if (paint.type === "IMAGE") {
952
+ return { type: "IMAGE", imageHash: paint.imageHash, scaleMode: paint.scaleMode };
953
+ }
954
+ if (paint.type === "GRADIENT_LINEAR" || paint.type === "GRADIENT_RADIAL" || paint.type === "GRADIENT_ANGULAR" || paint.type === "GRADIENT_DIAMOND") {
955
+ return {
956
+ type: paint.type,
957
+ stops: paint.gradientStops.map((s) => ({ color: rgbToHex(s.color), position: s.position }))
958
+ };
959
+ }
960
+ return { type: paint.type };
961
+ }
962
+ function rgbToHex(color) {
963
+ const r = Math.round(color.r * 255).toString(16).padStart(2, "0");
964
+ const g = Math.round(color.g * 255).toString(16).padStart(2, "0");
965
+ const b = Math.round(color.b * 255).toString(16).padStart(2, "0");
966
+ return `#${r}${g}${b}`.toUpperCase();
967
+ }
968
+ function expandHex(hex) {
969
+ const clean = hex.replace("#", "");
970
+ if (clean.length === 3) {
971
+ return clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
972
+ }
973
+ if (clean.length === 4) {
974
+ return clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2] + clean[3] + clean[3];
975
+ }
976
+ return clean;
977
+ }
978
+ function hexToRgb(hex) {
979
+ const clean = expandHex(hex);
980
+ return {
981
+ r: parseInt(clean.slice(0, 2), 16) / 255,
982
+ g: parseInt(clean.slice(2, 4), 16) / 255,
983
+ b: parseInt(clean.slice(4, 6), 16) / 255
984
+ };
985
+ }
986
+ function hexToRgba(hex) {
987
+ const clean = expandHex(hex);
988
+ const hasAlpha = clean.length === 8;
989
+ return {
990
+ r: parseInt(clean.slice(0, 2), 16) / 255,
991
+ g: parseInt(clean.slice(2, 4), 16) / 255,
992
+ b: parseInt(clean.slice(4, 6), 16) / 255,
993
+ a: hasAlpha ? parseInt(clean.slice(6, 8), 16) / 255 : 1
994
+ };
995
+ }
996
+ })();