mcp-figma-toolkit 1.0.9 → 1.0.11
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/package.json +1 -1
- package/plugin/plugin.js +156 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-figma-toolkit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "MCP server that enables AI agents to manipulate Figma documents - create shapes, text, styles, components, variables, and more",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/server.js",
|
package/plugin/plugin.js
CHANGED
|
@@ -27,16 +27,73 @@ function serializePaints(paints) {
|
|
|
27
27
|
base.hex = rgbToHex(p.color);
|
|
28
28
|
} else if (p.type && p.type.indexOf("GRADIENT") === 0 && p.gradientStops) {
|
|
29
29
|
base.gradientStops = p.gradientStops.map(function(s) {
|
|
30
|
-
|
|
30
|
+
var stop = {
|
|
31
31
|
position: s.position,
|
|
32
32
|
hex: s.color ? rgbToHex(s.color) : null,
|
|
33
33
|
opacity: s.color && s.color.a != null ? s.color.a : 1
|
|
34
34
|
};
|
|
35
|
+
if (s.boundVariables) {
|
|
36
|
+
var sbv = {};
|
|
37
|
+
var stopBVKeys = ["color"];
|
|
38
|
+
for (var sk in s.boundVariables) {
|
|
39
|
+
var sb = s.boundVariables[sk];
|
|
40
|
+
if (sb && sb.id) {
|
|
41
|
+
sbv[sk] = { type: sb.type, id: sb.id };
|
|
42
|
+
try {
|
|
43
|
+
var sv = figma.variables.getVariableById(sb.id);
|
|
44
|
+
if (sv) { sbv[sk].name = sv.name; sbv[sk].resolvedType = sv.resolvedType; }
|
|
45
|
+
} catch (_) {}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (var si = 0; si < stopBVKeys.length; si++) {
|
|
49
|
+
var spk = stopBVKeys[si];
|
|
50
|
+
if (!(spk in sbv)) {
|
|
51
|
+
try {
|
|
52
|
+
var spb = s.boundVariables[spk];
|
|
53
|
+
if (spb && spb.id) {
|
|
54
|
+
sbv[spk] = { type: spb.type, id: spb.id };
|
|
55
|
+
var spv = figma.variables.getVariableById(spb.id);
|
|
56
|
+
if (spv) { sbv[spk].name = spv.name; sbv[spk].resolvedType = spv.resolvedType; }
|
|
57
|
+
}
|
|
58
|
+
} catch (_) {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (Object.keys(sbv).length > 0) stop.boundVariables = sbv;
|
|
62
|
+
}
|
|
63
|
+
return stop;
|
|
35
64
|
});
|
|
36
65
|
} else if (p.type === "IMAGE") {
|
|
37
66
|
base.scaleMode = p.scaleMode;
|
|
38
67
|
base.imageHash = p.imageHash;
|
|
39
68
|
}
|
|
69
|
+
if (p.boundVariables) {
|
|
70
|
+
var pbv = {};
|
|
71
|
+
var paintBVKeys = ["color", "opacity"];
|
|
72
|
+
for (var pk in p.boundVariables) {
|
|
73
|
+
var pb = p.boundVariables[pk];
|
|
74
|
+
if (pb && pb.id) {
|
|
75
|
+
pbv[pk] = { type: pb.type, id: pb.id };
|
|
76
|
+
try {
|
|
77
|
+
var pv = figma.variables.getVariableById(pb.id);
|
|
78
|
+
if (pv) { pbv[pk].name = pv.name; pbv[pk].resolvedType = pv.resolvedType; }
|
|
79
|
+
} catch (_) {}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (var pi = 0; pi < paintBVKeys.length; pi++) {
|
|
83
|
+
var ppk = paintBVKeys[pi];
|
|
84
|
+
if (!(ppk in pbv)) {
|
|
85
|
+
try {
|
|
86
|
+
var ppb = p.boundVariables[ppk];
|
|
87
|
+
if (ppb && ppb.id) {
|
|
88
|
+
pbv[ppk] = { type: ppb.type, id: ppb.id };
|
|
89
|
+
var ppv = figma.variables.getVariableById(ppb.id);
|
|
90
|
+
if (ppv) { pbv[ppk].name = ppv.name; pbv[ppk].resolvedType = ppv.resolvedType; }
|
|
91
|
+
}
|
|
92
|
+
} catch (_) {}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (Object.keys(pbv).length > 0) base.boundVariables = pbv;
|
|
96
|
+
}
|
|
40
97
|
return base;
|
|
41
98
|
});
|
|
42
99
|
}
|
|
@@ -53,6 +110,22 @@ function serializeEffects(effects) {
|
|
|
53
110
|
base.hex = rgbToHex(e.color);
|
|
54
111
|
base.opacity = e.color.a != null ? e.color.a : 1;
|
|
55
112
|
}
|
|
113
|
+
if (e.boundVariables) {
|
|
114
|
+
var ebv = {};
|
|
115
|
+
var ebvKeys = ["radius", "spread", "color", "offsetX", "offsetY"];
|
|
116
|
+
for (var ei = 0; ei < ebvKeys.length; ei++) {
|
|
117
|
+
var ek = ebvKeys[ei];
|
|
118
|
+
try {
|
|
119
|
+
var eb = e.boundVariables[ek];
|
|
120
|
+
if (eb && eb.id) {
|
|
121
|
+
ebv[ek] = { type: eb.type, id: eb.id };
|
|
122
|
+
var ev = figma.variables.getVariableById(eb.id);
|
|
123
|
+
if (ev) { ebv[ek].name = ev.name; ebv[ek].resolvedType = ev.resolvedType; }
|
|
124
|
+
}
|
|
125
|
+
} catch (_) {}
|
|
126
|
+
}
|
|
127
|
+
if (Object.keys(ebv).length > 0) base.boundVariables = ebv;
|
|
128
|
+
}
|
|
56
129
|
return base;
|
|
57
130
|
});
|
|
58
131
|
}
|
|
@@ -718,6 +791,45 @@ function getNodeInfo({ nodeId }) {
|
|
|
718
791
|
try { info.effects = serializeEffects(node.effects); } catch (_) {}
|
|
719
792
|
}
|
|
720
793
|
|
|
794
|
+
// Constraints
|
|
795
|
+
if ("constraints" in node) {
|
|
796
|
+
info.constraints = {
|
|
797
|
+
horizontal: node.constraints.horizontal,
|
|
798
|
+
vertical: node.constraints.vertical
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Clips content (for frames)
|
|
803
|
+
if ("clipsContent" in node) info.clipsContent = node.clipsContent;
|
|
804
|
+
|
|
805
|
+
// Layout positioning within auto-layout parent
|
|
806
|
+
if ("layoutAlign" in node) info.layoutAlign = node.layoutAlign;
|
|
807
|
+
if ("layoutGrow" in node) info.layoutGrow = node.layoutGrow;
|
|
808
|
+
if ("layoutPositioning" in node) info.layoutPositioning = node.layoutPositioning;
|
|
809
|
+
|
|
810
|
+
// Style IDs (paint styles, text styles, effect styles)
|
|
811
|
+
if ("fillStyleId" in node && node.fillStyleId && node.fillStyleId !== "") {
|
|
812
|
+
info.fillStyleId = node.fillStyleId;
|
|
813
|
+
}
|
|
814
|
+
if ("strokeStyleId" in node && node.strokeStyleId && node.strokeStyleId !== "") {
|
|
815
|
+
info.strokeStyleId = node.strokeStyleId;
|
|
816
|
+
}
|
|
817
|
+
if ("effectStyleId" in node && node.effectStyleId && node.effectStyleId !== "") {
|
|
818
|
+
info.effectStyleId = node.effectStyleId;
|
|
819
|
+
}
|
|
820
|
+
if ("textStyleId" in node && node.textStyleId && node.textStyleId !== "") {
|
|
821
|
+
info.textStyleId = node.textStyleId;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Mask
|
|
825
|
+
if ("isMask" in node) info.isMask = node.isMask;
|
|
826
|
+
|
|
827
|
+
// Min/max size
|
|
828
|
+
if ("minWidth" in node && node.minWidth != null) info.minWidth = node.minWidth;
|
|
829
|
+
if ("maxWidth" in node && node.maxWidth != null) info.maxWidth = node.maxWidth;
|
|
830
|
+
if ("minHeight" in node && node.minHeight != null) info.minHeight = node.minHeight;
|
|
831
|
+
if ("maxHeight" in node && node.maxHeight != null) info.maxHeight = node.maxHeight;
|
|
832
|
+
|
|
721
833
|
// Auto-layout properties
|
|
722
834
|
if ("layoutMode" in node && node.layoutMode !== "NONE") {
|
|
723
835
|
info.autoLayout = {
|
|
@@ -731,7 +843,8 @@ function getNodeInfo({ nodeId }) {
|
|
|
731
843
|
primaryAxisAlignItems: node.primaryAxisAlignItems,
|
|
732
844
|
counterAxisAlignItems: node.counterAxisAlignItems,
|
|
733
845
|
layoutSizingHorizontal: node.layoutSizingHorizontal,
|
|
734
|
-
layoutSizingVertical: node.layoutSizingVertical
|
|
846
|
+
layoutSizingVertical: node.layoutSizingVertical,
|
|
847
|
+
layoutWrap: node.layoutWrap
|
|
735
848
|
};
|
|
736
849
|
}
|
|
737
850
|
|
|
@@ -783,10 +896,25 @@ function getNodeInfo({ nodeId }) {
|
|
|
783
896
|
try {
|
|
784
897
|
var bv = node.boundVariables;
|
|
785
898
|
var bvResult = {};
|
|
786
|
-
|
|
787
|
-
|
|
899
|
+
var knownBVKeys = [
|
|
900
|
+
"fills", "strokes", "effects",
|
|
901
|
+
"layoutGrids", "componentProperties",
|
|
902
|
+
"fontSize", "fontFamily", "fontStyle", "fontWeight",
|
|
903
|
+
"lineHeight", "letterSpacing", "paragraphSpacing", "paragraphIndent",
|
|
904
|
+
"textCase", "textDecoration",
|
|
905
|
+
"visible", "opacity",
|
|
906
|
+
"topLeftRadius", "topRightRadius", "bottomLeftRadius", "bottomRightRadius",
|
|
907
|
+
"itemSpacing", "counterAxisSpacing",
|
|
908
|
+
"paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
|
|
909
|
+
"strokeWeight", "strokeTopWeight", "strokeBottomWeight", "strokeLeftWeight", "strokeRightWeight",
|
|
910
|
+
"width", "height",
|
|
911
|
+
"minWidth", "maxWidth", "minHeight", "maxHeight",
|
|
912
|
+
"characters"
|
|
913
|
+
];
|
|
914
|
+
|
|
915
|
+
function extractBinding(binding) {
|
|
788
916
|
if (Array.isArray(binding)) {
|
|
789
|
-
|
|
917
|
+
return binding.map(function(b) {
|
|
790
918
|
var entry = { type: b.type, id: b.id };
|
|
791
919
|
try {
|
|
792
920
|
var v = figma.variables.getVariableById(b.id);
|
|
@@ -795,10 +923,31 @@ function getNodeInfo({ nodeId }) {
|
|
|
795
923
|
return entry;
|
|
796
924
|
});
|
|
797
925
|
} else if (binding && binding.id) {
|
|
798
|
-
|
|
926
|
+
var single = { type: binding.type, id: binding.id };
|
|
799
927
|
try {
|
|
800
928
|
var v2 = figma.variables.getVariableById(binding.id);
|
|
801
|
-
if (v2) {
|
|
929
|
+
if (v2) { single.name = v2.name; single.resolvedType = v2.resolvedType; }
|
|
930
|
+
} catch (_) {}
|
|
931
|
+
return single;
|
|
932
|
+
}
|
|
933
|
+
return null;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// First try for...in (catches any enumerable keys)
|
|
937
|
+
for (var key in bv) {
|
|
938
|
+
var result = extractBinding(bv[key]);
|
|
939
|
+
if (result != null) bvResult[key] = result;
|
|
940
|
+
}
|
|
941
|
+
// Supplement with explicit key access (Figma proxies may not enumerate all keys)
|
|
942
|
+
for (var ki = 0; ki < knownBVKeys.length; ki++) {
|
|
943
|
+
var bvKey = knownBVKeys[ki];
|
|
944
|
+
if (!(bvKey in bvResult)) {
|
|
945
|
+
try {
|
|
946
|
+
var val = bv[bvKey];
|
|
947
|
+
if (val != null) {
|
|
948
|
+
var r = extractBinding(val);
|
|
949
|
+
if (r != null) bvResult[bvKey] = r;
|
|
950
|
+
}
|
|
802
951
|
} catch (_) {}
|
|
803
952
|
}
|
|
804
953
|
}
|