@clovis500c/figma-bridge 1.8.0 → 1.13.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.
- package/README.md +49 -13
- package/dist/server.js +2256 -351
- package/package.json +7 -3
- package/plugin/code.js +577 -20
- package/plugin/manifest.json +2 -1
- package/plugin/ui.html +24 -3
package/plugin/code.js
CHANGED
|
@@ -1177,6 +1177,186 @@
|
|
|
1177
1177
|
return a.x >= b.x - 0.5 && a.y >= b.y - 0.5 && a.x + a.width <= b.x + b.width + 0.5 && a.y + a.height <= b.y + b.height + 0.5;
|
|
1178
1178
|
}
|
|
1179
1179
|
|
|
1180
|
+
// plugin/lib/figjam.ts
|
|
1181
|
+
var EDITORS = { sticky: "figjam", shape: "figjam", connector: "figjam", table: "figjam", codeblock: "figjam", slide: "slides" };
|
|
1182
|
+
var EDITOR_NAMES = { figma: "Figma Design", figjam: "FigJam", slides: "Figma Slides", dev: "Dev Mode" };
|
|
1183
|
+
function requireEditor(type, path) {
|
|
1184
|
+
const wanted = EDITORS[type];
|
|
1185
|
+
if (wanted && figma.editorType !== wanted) {
|
|
1186
|
+
throw codeError(path + ': "' + type + '" needs a ' + EDITOR_NAMES[wanted] + " file; this file is open in " + (EDITOR_NAMES[figma.editorType] || figma.editorType) + ".", "WRONG_EDITOR");
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
var STICKY_COLORS = {
|
|
1190
|
+
white: "#FFFFFF",
|
|
1191
|
+
gray: "#E6E6E6",
|
|
1192
|
+
grey: "#E6E6E6",
|
|
1193
|
+
green: "#B3EFBD",
|
|
1194
|
+
teal: "#B3F4EF",
|
|
1195
|
+
blue: "#A8DAFF",
|
|
1196
|
+
violet: "#D3BDFF",
|
|
1197
|
+
purple: "#D3BDFF",
|
|
1198
|
+
pink: "#FFA8DB",
|
|
1199
|
+
red: "#FFB8A8",
|
|
1200
|
+
orange: "#FFD3A8",
|
|
1201
|
+
yellow: "#FFE299"
|
|
1202
|
+
};
|
|
1203
|
+
function solid(color) {
|
|
1204
|
+
const hex = STICKY_COLORS[String(color).toLowerCase()] || color;
|
|
1205
|
+
const c = parseHex(hex);
|
|
1206
|
+
return { type: "SOLID", color: { r: c.r, g: c.g, b: c.b }, opacity: c.a };
|
|
1207
|
+
}
|
|
1208
|
+
async function setSublayerText(sub, text, opts) {
|
|
1209
|
+
const font = sub.fontName;
|
|
1210
|
+
if (font !== figma.mixed)
|
|
1211
|
+
await loadFont(font);
|
|
1212
|
+
else if (sub.characters.length) {
|
|
1213
|
+
const fonts = sub.getRangeAllFontNames(0, sub.characters.length);
|
|
1214
|
+
for (let i = 0;i < fonts.length; i++)
|
|
1215
|
+
await loadFont(fonts[i]);
|
|
1216
|
+
}
|
|
1217
|
+
if (opts && opts.font) {
|
|
1218
|
+
await loadFont(opts.font);
|
|
1219
|
+
sub.fontName = opts.font;
|
|
1220
|
+
}
|
|
1221
|
+
sub.characters = String(text === undefined || text === null ? "" : text);
|
|
1222
|
+
if (opts && typeof opts.size === "number")
|
|
1223
|
+
sub.fontSize = opts.size;
|
|
1224
|
+
if (opts && opts.color)
|
|
1225
|
+
sub.fills = [solid(opts.color)];
|
|
1226
|
+
}
|
|
1227
|
+
async function createSticky(s) {
|
|
1228
|
+
const n = figma.createSticky();
|
|
1229
|
+
if (s.wide)
|
|
1230
|
+
n.isWideWidth = true;
|
|
1231
|
+
if (s.author === false)
|
|
1232
|
+
n.authorVisible = false;
|
|
1233
|
+
if (s.color || s.fill)
|
|
1234
|
+
n.fills = [solid(s.color || s.fill)];
|
|
1235
|
+
await setSublayerText(n.text, s.text);
|
|
1236
|
+
return n;
|
|
1237
|
+
}
|
|
1238
|
+
var SHAPES = {
|
|
1239
|
+
square: "SQUARE",
|
|
1240
|
+
rect: "SQUARE",
|
|
1241
|
+
rectangle: "SQUARE",
|
|
1242
|
+
rounded: "ROUNDED_RECTANGLE",
|
|
1243
|
+
roundedrectangle: "ROUNDED_RECTANGLE",
|
|
1244
|
+
ellipse: "ELLIPSE",
|
|
1245
|
+
circle: "ELLIPSE",
|
|
1246
|
+
diamond: "DIAMOND",
|
|
1247
|
+
decision: "DIAMOND",
|
|
1248
|
+
triangle: "TRIANGLE_UP",
|
|
1249
|
+
triangleup: "TRIANGLE_UP",
|
|
1250
|
+
triangledown: "TRIANGLE_DOWN",
|
|
1251
|
+
parallelogram: "PARALLELOGRAM_RIGHT",
|
|
1252
|
+
parallelogramleft: "PARALLELOGRAM_LEFT",
|
|
1253
|
+
database: "ENG_DATABASE",
|
|
1254
|
+
cylinder: "ENG_DATABASE",
|
|
1255
|
+
queue: "ENG_QUEUE",
|
|
1256
|
+
file: "ENG_FILE",
|
|
1257
|
+
folder: "ENG_FOLDER",
|
|
1258
|
+
trapezoid: "TRAPEZOID",
|
|
1259
|
+
process: "PREDEFINED_PROCESS",
|
|
1260
|
+
subroutine: "PREDEFINED_PROCESS",
|
|
1261
|
+
shield: "SHIELD",
|
|
1262
|
+
document: "DOCUMENT_SINGLE",
|
|
1263
|
+
documents: "DOCUMENT_MULTIPLE",
|
|
1264
|
+
input: "MANUAL_INPUT",
|
|
1265
|
+
hexagon: "HEXAGON",
|
|
1266
|
+
chevron: "CHEVRON",
|
|
1267
|
+
pentagon: "PENTAGON",
|
|
1268
|
+
octagon: "OCTAGON",
|
|
1269
|
+
star: "STAR",
|
|
1270
|
+
plus: "PLUS",
|
|
1271
|
+
arrowleft: "ARROW_LEFT",
|
|
1272
|
+
arrowright: "ARROW_RIGHT",
|
|
1273
|
+
junction: "SUMMING_JUNCTION",
|
|
1274
|
+
or: "OR",
|
|
1275
|
+
speech: "SPEECH_BUBBLE",
|
|
1276
|
+
speechbubble: "SPEECH_BUBBLE",
|
|
1277
|
+
storage: "INTERNAL_STORAGE"
|
|
1278
|
+
};
|
|
1279
|
+
async function createShape(s, path, warnings) {
|
|
1280
|
+
const n = figma.createShapeWithText();
|
|
1281
|
+
const key = String(s.shape || "rounded").toLowerCase().replace(/[^a-z]/g, "");
|
|
1282
|
+
const shape = SHAPES[key] || String(s.shape || "").toUpperCase();
|
|
1283
|
+
try {
|
|
1284
|
+
n.shapeType = shape;
|
|
1285
|
+
} catch (e) {
|
|
1286
|
+
warnings.push(path + ': unknown shape "' + s.shape + '", used a rounded rectangle');
|
|
1287
|
+
n.shapeType = "ROUNDED_RECTANGLE";
|
|
1288
|
+
}
|
|
1289
|
+
const w = typeof s.w === "number" ? s.w : typeof s.width === "number" ? s.width : 0;
|
|
1290
|
+
const h = typeof s.h === "number" ? s.h : typeof s.height === "number" ? s.height : 0;
|
|
1291
|
+
if (w || h)
|
|
1292
|
+
n.resize(w || n.width, h || n.height);
|
|
1293
|
+
if (s.fill !== undefined)
|
|
1294
|
+
n.fills = s.fill === null ? [] : [solid(s.fill)];
|
|
1295
|
+
if (s.stroke !== undefined)
|
|
1296
|
+
n.strokes = s.stroke === null ? [] : [solid(s.stroke)];
|
|
1297
|
+
if (typeof s.strokeWidth === "number")
|
|
1298
|
+
n.strokeWeight = s.strokeWidth;
|
|
1299
|
+
if (s.text !== undefined)
|
|
1300
|
+
await setSublayerText(n.text, s.text, { size: s.size, color: s.color });
|
|
1301
|
+
return n;
|
|
1302
|
+
}
|
|
1303
|
+
async function createTable(s, path) {
|
|
1304
|
+
const rows = Array.isArray(s.rows) ? s.rows : [];
|
|
1305
|
+
if (!rows.length)
|
|
1306
|
+
throw codeError(path + ': a table needs rows:[["Header 1","Header 2"],["a","b"]]', "BAD_ARGS");
|
|
1307
|
+
let cols = 0;
|
|
1308
|
+
for (let i = 0;i < rows.length; i++)
|
|
1309
|
+
cols = Math.max(cols, Array.isArray(rows[i]) ? rows[i].length : 1);
|
|
1310
|
+
const t = figma.createTable(rows.length, cols);
|
|
1311
|
+
for (let r = 0;r < rows.length; r++) {
|
|
1312
|
+
for (let c = 0;c < cols; c++) {
|
|
1313
|
+
const value = Array.isArray(rows[r]) ? rows[r][c] : c === 0 ? rows[r] : "";
|
|
1314
|
+
const cell = t.cellAt(r, c);
|
|
1315
|
+
await setSublayerText(cell.text, value === undefined ? "" : value);
|
|
1316
|
+
if (r === 0 && s.header !== false)
|
|
1317
|
+
cell.fills = [solid(s.headerFill || "#F2F2F2")];
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
return t;
|
|
1321
|
+
}
|
|
1322
|
+
var LANGUAGES = ["TYPESCRIPT", "CPP", "RUBY", "CSS", "JAVASCRIPT", "HTML", "JSON", "GRAPHQL", "PYTHON", "GO", "SQL", "SWIFT", "KOTLIN", "RUST", "BASH", "PLAINTEXT", "DART"];
|
|
1323
|
+
var LANGUAGE_ALIASES = { ts: "TYPESCRIPT", tsx: "TYPESCRIPT", js: "JAVASCRIPT", jsx: "JAVASCRIPT", "c++": "CPP", py: "PYTHON", sh: "BASH", shell: "BASH", text: "PLAINTEXT", txt: "PLAINTEXT", golang: "GO", rb: "RUBY" };
|
|
1324
|
+
function createCodeBlock(s) {
|
|
1325
|
+
const n = figma.createCodeBlock();
|
|
1326
|
+
const lang = String(s.language || "plaintext").toLowerCase();
|
|
1327
|
+
const code = LANGUAGE_ALIASES[lang] || lang.toUpperCase();
|
|
1328
|
+
n.codeLanguage = LANGUAGES.indexOf(code) !== -1 ? code : "PLAINTEXT";
|
|
1329
|
+
n.code = String(s.code === undefined ? "" : s.code);
|
|
1330
|
+
return n;
|
|
1331
|
+
}
|
|
1332
|
+
function createSlide() {
|
|
1333
|
+
return figma.createSlide();
|
|
1334
|
+
}
|
|
1335
|
+
var CAPS = { arrow: "ARROW_LINES", triangle: "ARROW_EQUILATERAL", filled: "TRIANGLE_FILLED", diamond: "DIAMOND_FILLED", circle: "CIRCLE_FILLED", none: "NONE" };
|
|
1336
|
+
var LINES = { elbowed: "ELBOWED", elbow: "ELBOWED", straight: "STRAIGHT", curved: "CURVED", curve: "CURVED" };
|
|
1337
|
+
var MAGNETS = ["AUTO", "TOP", "LEFT", "BOTTOM", "RIGHT", "CENTER", "NONE"];
|
|
1338
|
+
async function createConnector(s, fromId, toId) {
|
|
1339
|
+
const n = figma.createConnector();
|
|
1340
|
+
const magnet = function(v) {
|
|
1341
|
+
const m = String(v || "AUTO").toUpperCase();
|
|
1342
|
+
return MAGNETS.indexOf(m) !== -1 ? m : "AUTO";
|
|
1343
|
+
};
|
|
1344
|
+
n.connectorStart = { endpointNodeId: fromId, magnet: magnet(s.fromMagnet) };
|
|
1345
|
+
n.connectorEnd = { endpointNodeId: toId, magnet: magnet(s.toMagnet) };
|
|
1346
|
+
n.connectorLineType = LINES[String(s.line || "elbowed").toLowerCase()] || "ELBOWED";
|
|
1347
|
+
n.connectorEndStrokeCap = CAPS[String(s.endArrow === undefined ? "arrow" : s.endArrow).toLowerCase()] || "ARROW_LINES";
|
|
1348
|
+
n.connectorStartStrokeCap = CAPS[String(s.startArrow === undefined ? "none" : s.startArrow).toLowerCase()] || "NONE";
|
|
1349
|
+
if (s.color || s.stroke)
|
|
1350
|
+
n.strokes = [solid(s.color || s.stroke)];
|
|
1351
|
+
if (typeof s.strokeWidth === "number")
|
|
1352
|
+
n.strokeWeight = s.strokeWidth;
|
|
1353
|
+
if (s.dashed)
|
|
1354
|
+
n.dashPattern = [8, 6];
|
|
1355
|
+
if (s.label !== undefined && s.label !== "")
|
|
1356
|
+
await setSublayerText(n.text, s.label);
|
|
1357
|
+
return n;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1180
1360
|
// plugin/lib/prototype.ts
|
|
1181
1361
|
var TRIGGERS = {
|
|
1182
1362
|
click: "ON_CLICK",
|
|
@@ -1763,7 +1943,9 @@
|
|
|
1763
1943
|
const spec = p.spec;
|
|
1764
1944
|
if (!spec || typeof spec !== "object")
|
|
1765
1945
|
throw codeError("`spec` must be a node object or an array of node objects", "BAD_ARGS");
|
|
1766
|
-
const roots = Array.isArray(spec) ? spec :
|
|
1946
|
+
const roots = Array.isArray(spec) ? spec : Array.isArray(spec.slides) ? spec.slides.map(function(sl) {
|
|
1947
|
+
return Object.assign({ type: "slide" }, sl);
|
|
1948
|
+
}) : [spec];
|
|
1767
1949
|
const d = p.defaults || {};
|
|
1768
1950
|
const ctx = {
|
|
1769
1951
|
warnings: [],
|
|
@@ -1774,6 +1956,8 @@
|
|
|
1774
1956
|
defaults: { font: d.font || "Inter", color: d.color || "#111111", size: d.size || 14 },
|
|
1775
1957
|
binds: [],
|
|
1776
1958
|
reactions: [],
|
|
1959
|
+
connectors: [],
|
|
1960
|
+
keys: {},
|
|
1777
1961
|
requestId,
|
|
1778
1962
|
total: countNodes(roots),
|
|
1779
1963
|
lastProgress: 0
|
|
@@ -1786,7 +1970,7 @@
|
|
|
1786
1970
|
const node = await createNode(roots[i], parent, ctx, "spec" + (roots.length > 1 ? "[" + i + "]" : ""));
|
|
1787
1971
|
if (!node)
|
|
1788
1972
|
continue;
|
|
1789
|
-
if (!isAutoLayout(parent) && roots[i].x === undefined && roots[i].y === undefined) {
|
|
1973
|
+
if (node.type !== "SLIDE" && !isAutoLayout(parent) && roots[i].x === undefined && roots[i].y === undefined) {
|
|
1790
1974
|
if (cursorX === null) {
|
|
1791
1975
|
place(node, p.x, p.y);
|
|
1792
1976
|
} else {
|
|
@@ -1799,6 +1983,7 @@
|
|
|
1799
1983
|
}
|
|
1800
1984
|
if (ctx.reactions.length)
|
|
1801
1985
|
await applyBuildReactions(ctx.reactions, ctx.ids, ctx.warnings);
|
|
1986
|
+
const connectors = await createConnectors(ctx);
|
|
1802
1987
|
for (let i = 0;i < ctx.binds.length; i++)
|
|
1803
1988
|
ctx.warnings.push(ctx.binds[i].path + ": bind needs a component or componentSet ancestor, ignored");
|
|
1804
1989
|
if (p.select !== false && made.length && parent.type === "PAGE" && parent === figma.currentPage) {
|
|
@@ -1810,7 +1995,7 @@
|
|
|
1810
1995
|
rootIds: made.map(function(n) {
|
|
1811
1996
|
return n.id;
|
|
1812
1997
|
}),
|
|
1813
|
-
created: ctx.count,
|
|
1998
|
+
created: ctx.count + connectors,
|
|
1814
1999
|
ids: ctx.ids
|
|
1815
2000
|
};
|
|
1816
2001
|
if (ctx.warnings.length)
|
|
@@ -1891,7 +2076,7 @@
|
|
|
1891
2076
|
function nodeType(s) {
|
|
1892
2077
|
if (s.type) {
|
|
1893
2078
|
const t = String(s.type).toLowerCase().replace(/[_\s-]/g, "");
|
|
1894
|
-
return t === "rectangle" ? "rect" : t === "circle" ? "ellipse" : t === "variants" ? "componentset" : t;
|
|
2079
|
+
return t === "rectangle" ? "rect" : t === "circle" ? "ellipse" : t === "variants" ? "componentset" : t === "shapewithtext" ? "shape" : t === "code" ? "codeblock" : t;
|
|
1895
2080
|
}
|
|
1896
2081
|
if (Array.isArray(s.variants))
|
|
1897
2082
|
return "componentset";
|
|
@@ -1916,8 +2101,30 @@
|
|
|
1916
2101
|
throw codeError("Spec is too large (max " + MAX_NODES3 + " nodes). Split it into several build calls.", "TOO_LARGE");
|
|
1917
2102
|
checkCancelled(ctx.requestId);
|
|
1918
2103
|
const type = nodeType(s);
|
|
2104
|
+
requireEditor(type, path);
|
|
1919
2105
|
let node;
|
|
1920
2106
|
switch (type) {
|
|
2107
|
+
case "connector":
|
|
2108
|
+
ctx.connectors.push({ spec: s, parent, path });
|
|
2109
|
+
return null;
|
|
2110
|
+
case "sticky":
|
|
2111
|
+
node = await createSticky(s);
|
|
2112
|
+
break;
|
|
2113
|
+
case "shape":
|
|
2114
|
+
node = await createShape(s, path, ctx.warnings);
|
|
2115
|
+
break;
|
|
2116
|
+
case "table":
|
|
2117
|
+
node = await createTable(s, path);
|
|
2118
|
+
break;
|
|
2119
|
+
case "codeblock":
|
|
2120
|
+
node = createCodeBlock(s);
|
|
2121
|
+
break;
|
|
2122
|
+
case "section":
|
|
2123
|
+
node = figma.createSection();
|
|
2124
|
+
break;
|
|
2125
|
+
case "slide":
|
|
2126
|
+
node = createSlide();
|
|
2127
|
+
break;
|
|
1921
2128
|
case "frame":
|
|
1922
2129
|
case "component":
|
|
1923
2130
|
node = type === "component" ? figma.createComponent() : figma.createFrame();
|
|
@@ -1954,8 +2161,11 @@
|
|
|
1954
2161
|
}
|
|
1955
2162
|
ctx.count++;
|
|
1956
2163
|
progress(ctx);
|
|
1957
|
-
parent.
|
|
2164
|
+
if (type !== "slide" || parent.type !== "PAGE")
|
|
2165
|
+
parent.appendChild(node);
|
|
1958
2166
|
const parentAuto = isAutoLayout(parent);
|
|
2167
|
+
if (s.key !== undefined)
|
|
2168
|
+
ctx.keys[String(s.key)] = node.id;
|
|
1959
2169
|
if (s.bind)
|
|
1960
2170
|
ctx.binds.push({ node, bind: s.bind, path });
|
|
1961
2171
|
const bindStart = ctx.binds.length;
|
|
@@ -1965,6 +2175,10 @@
|
|
|
1965
2175
|
node.name = "icon/" + s.icon;
|
|
1966
2176
|
if (type === "frame" || type === "component")
|
|
1967
2177
|
await setupFrame(node, s, ctx, path);
|
|
2178
|
+
else if (type === "slide")
|
|
2179
|
+
await setupFrame(node, s, ctx, path, true);
|
|
2180
|
+
else if (type === "section")
|
|
2181
|
+
await setupSection(node, s, ctx, path);
|
|
1968
2182
|
else if (type === "componentset")
|
|
1969
2183
|
await setupLayout(node, Object.assign({ layout: "row", wrap: true, gap: 16, padding: 16 }, s), ctx, path);
|
|
1970
2184
|
else if (type === "text")
|
|
@@ -1974,7 +2188,7 @@
|
|
|
1974
2188
|
else if (type === "line") {
|
|
1975
2189
|
node.resize(num(s.w, s.width, 100), 0);
|
|
1976
2190
|
if (s.stroke === undefined)
|
|
1977
|
-
node.strokes = [
|
|
2191
|
+
node.strokes = [solid2(ctx.defaults.color)];
|
|
1978
2192
|
} else if ((type === "rect" || type === "ellipse") && s.w === undefined && s.width === undefined && s.size === undefined) {
|
|
1979
2193
|
node.resize(100, 100);
|
|
1980
2194
|
}
|
|
@@ -1982,14 +2196,18 @@
|
|
|
1982
2196
|
await overrideTexts(node, s.text, ctx);
|
|
1983
2197
|
if (type === "componentset" && s.stroke === undefined) {
|
|
1984
2198
|
const set = node;
|
|
1985
|
-
set.strokes = [
|
|
2199
|
+
set.strokes = [solid2("#9747FF")];
|
|
1986
2200
|
set.dashPattern = [10, 5];
|
|
1987
2201
|
set.cornerRadius = 5;
|
|
1988
2202
|
}
|
|
1989
|
-
|
|
2203
|
+
if (FIXED_LOOK.indexOf(type) === -1)
|
|
2204
|
+
await applyVisuals(node, s, ctx, type);
|
|
2205
|
+
else if (typeof s.opacity === "number")
|
|
2206
|
+
node.opacity = s.opacity;
|
|
1990
2207
|
if (type === "text" && Array.isArray(s.spans))
|
|
1991
2208
|
await applySpans(node, s, ctx, path);
|
|
1992
|
-
|
|
2209
|
+
if (FIXED_LOOK.indexOf(type) === -1 && type !== "section" && type !== "slide")
|
|
2210
|
+
applySize(node, s, parentAuto, type, ctx, path);
|
|
1993
2211
|
if (type === "component" && !s.variantOf)
|
|
1994
2212
|
await addProperties(node, s.properties, ctx.binds.splice(bindStart), ctx, path);
|
|
1995
2213
|
if (parent.layoutMode === "GRID")
|
|
@@ -2004,7 +2222,7 @@
|
|
|
2004
2222
|
}
|
|
2005
2223
|
if (s.grow && parentAuto)
|
|
2006
2224
|
node.layoutGrow = 1;
|
|
2007
|
-
if (typeof s.rotation === "number")
|
|
2225
|
+
if (typeof s.rotation === "number" && "rotation" in node)
|
|
2008
2226
|
node.rotation = s.rotation;
|
|
2009
2227
|
if (s.visible === false)
|
|
2010
2228
|
node.visible = false;
|
|
@@ -2023,20 +2241,83 @@
|
|
|
2023
2241
|
}
|
|
2024
2242
|
return node;
|
|
2025
2243
|
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2244
|
+
var FIXED_LOOK = ["sticky", "shape", "table", "codeblock"];
|
|
2245
|
+
async function setupFrame(f, s, ctx, path, keepFills) {
|
|
2246
|
+
if (!keepFills)
|
|
2247
|
+
f.fills = [];
|
|
2028
2248
|
await setupLayout(f, s, ctx, path);
|
|
2029
2249
|
const children = Array.isArray(s.children) ? s.children : [];
|
|
2030
2250
|
for (let i = 0;i < children.length; i++) {
|
|
2031
2251
|
try {
|
|
2032
2252
|
await createNode(children[i], f, ctx, path + ".children[" + i + "]");
|
|
2033
2253
|
} catch (e) {
|
|
2034
|
-
if (e.code === "TOO_LARGE" || e.code === "CANCELLED")
|
|
2254
|
+
if (e.code === "TOO_LARGE" || e.code === "CANCELLED" || e.code === "WRONG_EDITOR")
|
|
2035
2255
|
throw e;
|
|
2036
2256
|
ctx.warnings.push(path + ".children[" + i + "]: " + (e.message || e));
|
|
2037
2257
|
}
|
|
2038
2258
|
}
|
|
2039
2259
|
}
|
|
2260
|
+
async function setupSection(sec, s, ctx, path) {
|
|
2261
|
+
const children = Array.isArray(s.children) ? s.children : [];
|
|
2262
|
+
const made = [];
|
|
2263
|
+
for (let i = 0;i < children.length; i++) {
|
|
2264
|
+
try {
|
|
2265
|
+
const n = await createNode(children[i], sec, ctx, path + ".children[" + i + "]");
|
|
2266
|
+
if (n)
|
|
2267
|
+
made.push(n);
|
|
2268
|
+
} catch (e) {
|
|
2269
|
+
if (e.code === "TOO_LARGE" || e.code === "CANCELLED" || e.code === "WRONG_EDITOR")
|
|
2270
|
+
throw e;
|
|
2271
|
+
ctx.warnings.push(path + ".children[" + i + "]: " + (e.message || e));
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
const w = num(s.w, s.width, 0);
|
|
2275
|
+
const h = num(s.h, s.height, 0);
|
|
2276
|
+
if (w && h) {
|
|
2277
|
+
sec.resizeWithoutConstraints(w, h);
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
const pad = typeof s.padding === "number" ? s.padding : 40;
|
|
2281
|
+
let right = 0;
|
|
2282
|
+
let bottom = 0;
|
|
2283
|
+
for (let i = 0;i < made.length; i++) {
|
|
2284
|
+
right = Math.max(right, made[i].x + made[i].width);
|
|
2285
|
+
bottom = Math.max(bottom, made[i].y + made[i].height);
|
|
2286
|
+
}
|
|
2287
|
+
sec.resizeWithoutConstraints(Math.max(w || right + pad, 100), Math.max(h || bottom + pad, 100));
|
|
2288
|
+
}
|
|
2289
|
+
async function createConnectors(ctx) {
|
|
2290
|
+
let made = 0;
|
|
2291
|
+
for (let i = 0;i < ctx.connectors.length; i++) {
|
|
2292
|
+
const c = ctx.connectors[i];
|
|
2293
|
+
const ends = [];
|
|
2294
|
+
const sides = ["from", "to"];
|
|
2295
|
+
for (let k = 0;k < 2; k++) {
|
|
2296
|
+
const ref = c.spec[sides[k]];
|
|
2297
|
+
let id = ref === undefined ? "" : ctx.keys[String(ref)] || ctx.ids[String(ref)] || "";
|
|
2298
|
+
if (!id && ref !== undefined && /^[\dI;:]+$/.test(String(ref))) {
|
|
2299
|
+
const n = await figma.getNodeByIdAsync(String(ref));
|
|
2300
|
+
if (n)
|
|
2301
|
+
id = n.id;
|
|
2302
|
+
}
|
|
2303
|
+
ends.push(id);
|
|
2304
|
+
}
|
|
2305
|
+
if (!ends[0] || !ends[1]) {
|
|
2306
|
+
ctx.warnings.push(c.path + ': connector ends not found (from "' + c.spec.from + '", to "' + c.spec.to + '")');
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
try {
|
|
2310
|
+
const n = await createConnector(c.spec, ends[0], ends[1]);
|
|
2311
|
+
c.parent.appendChild(n);
|
|
2312
|
+
if (c.spec.name)
|
|
2313
|
+
n.name = String(c.spec.name);
|
|
2314
|
+
made++;
|
|
2315
|
+
} catch (e) {
|
|
2316
|
+
ctx.warnings.push(c.path + ": " + (e.message || e));
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
return made;
|
|
2320
|
+
}
|
|
2040
2321
|
async function setupLayout(f, s, ctx, path) {
|
|
2041
2322
|
const layout = String(s.layout || s.direction || "").toLowerCase();
|
|
2042
2323
|
let mode = layout === "row" || layout === "horizontal" ? "HORIZONTAL" : layout === "column" || layout === "col" || layout === "vertical" ? "VERTICAL" : layout === "grid" ? "GRID" : "NONE";
|
|
@@ -2070,7 +2351,7 @@
|
|
|
2070
2351
|
if (s.rowGap !== undefined)
|
|
2071
2352
|
await setNumber(f, "counterAxisSpacing", s.rowGap);
|
|
2072
2353
|
}
|
|
2073
|
-
} else if (s.w === undefined && s.width === undefined && s.size === undefined) {
|
|
2354
|
+
} else if (s.w === undefined && s.width === undefined && s.size === undefined && f.type !== "SLIDE") {
|
|
2074
2355
|
f.resize(100, 100);
|
|
2075
2356
|
}
|
|
2076
2357
|
}
|
|
@@ -2210,7 +2491,7 @@
|
|
|
2210
2491
|
t.maxLines = s.maxLines;
|
|
2211
2492
|
}
|
|
2212
2493
|
if (s.fill === undefined && s.color === undefined && !s.textStyle)
|
|
2213
|
-
t.fills = [
|
|
2494
|
+
t.fills = [solid2(ctx.defaults.color)];
|
|
2214
2495
|
if (s.textStyle) {
|
|
2215
2496
|
const style = await findStyle("text", stripPrefix(s.textStyle));
|
|
2216
2497
|
await loadFont(style.fontName);
|
|
@@ -2497,7 +2778,7 @@
|
|
|
2497
2778
|
function stripPrefix(v) {
|
|
2498
2779
|
return String(v).replace(/^(style|var):/, "");
|
|
2499
2780
|
}
|
|
2500
|
-
function
|
|
2781
|
+
function solid2(hex) {
|
|
2501
2782
|
const c = parseHex(hex);
|
|
2502
2783
|
return { type: "SOLID", color: { r: c.r, g: c.g, b: c.b }, opacity: c.a };
|
|
2503
2784
|
}
|
|
@@ -2521,9 +2802,9 @@
|
|
|
2521
2802
|
if (typeof v === "string") {
|
|
2522
2803
|
if (v.indexOf("var:") === 0) {
|
|
2523
2804
|
const variable = await findVariable(stripPrefix(v));
|
|
2524
|
-
return figma.variables.setBoundVariableForPaint(
|
|
2805
|
+
return figma.variables.setBoundVariableForPaint(solid2("#000000"), "color", variable);
|
|
2525
2806
|
}
|
|
2526
|
-
return
|
|
2807
|
+
return solid2(v);
|
|
2527
2808
|
}
|
|
2528
2809
|
if (v && Array.isArray(v.gradient)) {
|
|
2529
2810
|
const stops = v.gradient.map(function(c, i, all) {
|
|
@@ -3639,6 +3920,243 @@
|
|
|
3639
3920
|
return out;
|
|
3640
3921
|
}
|
|
3641
3922
|
|
|
3923
|
+
// plugin/lib/roblox.ts
|
|
3924
|
+
var MAX_NODES5 = 2000;
|
|
3925
|
+
var VECTORS = ["VECTOR", "BOOLEAN_OPERATION", "STAR", "POLYGON", "LINE"];
|
|
3926
|
+
async function robloxTree(p) {
|
|
3927
|
+
const node = p.nodeId ? await getNode(p.nodeId) : figma.currentPage.selection[0];
|
|
3928
|
+
if (!node)
|
|
3929
|
+
throw codeError("No nodeId given and nothing is selected", "BAD_ARGS");
|
|
3930
|
+
if (node.type === "PAGE" || node.type === "DOCUMENT")
|
|
3931
|
+
throw codeError("Export a frame, not a page", "BAD_ARGS");
|
|
3932
|
+
const ctx = { count: 0, truncated: false, images: {}, imageBytes: 0 };
|
|
3933
|
+
const tree = await walk2(node, null, ctx);
|
|
3934
|
+
return { tree, images: ctx.images, nodes: ctx.count, truncated: ctx.truncated, fileName: figma.root.name };
|
|
3935
|
+
}
|
|
3936
|
+
function paints(list) {
|
|
3937
|
+
if (!list || list === figma.mixed)
|
|
3938
|
+
return [];
|
|
3939
|
+
const out = [];
|
|
3940
|
+
for (let i = 0;i < list.length; i++) {
|
|
3941
|
+
const pt = list[i];
|
|
3942
|
+
if (pt.visible === false)
|
|
3943
|
+
continue;
|
|
3944
|
+
const o = { type: pt.type, opacity: pt.opacity === undefined ? 1 : round(pt.opacity) };
|
|
3945
|
+
if (pt.type === "SOLID")
|
|
3946
|
+
o.color = toHex(pt.color);
|
|
3947
|
+
else if (pt.type.indexOf("GRADIENT") === 0) {
|
|
3948
|
+
const t = pt.gradientTransform;
|
|
3949
|
+
o.angle = round(Math.atan2(t[0][1], t[0][0]) * 180 / Math.PI);
|
|
3950
|
+
o.stops = pt.gradientStops.map(function(s) {
|
|
3951
|
+
return { color: toHex({ r: s.color.r, g: s.color.g, b: s.color.b }), alpha: round(s.color.a), at: round(s.position) };
|
|
3952
|
+
});
|
|
3953
|
+
} else if (pt.type === "IMAGE") {
|
|
3954
|
+
o.imageHash = pt.imageHash;
|
|
3955
|
+
o.scaleMode = pt.scaleMode;
|
|
3956
|
+
}
|
|
3957
|
+
out.push(o);
|
|
3958
|
+
}
|
|
3959
|
+
return out;
|
|
3960
|
+
}
|
|
3961
|
+
function vectorOnly2(n) {
|
|
3962
|
+
if (VECTORS.indexOf(n.type) !== -1)
|
|
3963
|
+
return true;
|
|
3964
|
+
if (n.type === "TEXT" || !("children" in n) || !n.children.length)
|
|
3965
|
+
return false;
|
|
3966
|
+
for (let i = 0;i < n.children.length; i++)
|
|
3967
|
+
if (n.children[i].visible && !vectorOnly2(n.children[i]))
|
|
3968
|
+
return false;
|
|
3969
|
+
return true;
|
|
3970
|
+
}
|
|
3971
|
+
async function walk2(n, parent, ctx) {
|
|
3972
|
+
ctx.count++;
|
|
3973
|
+
const box = n.absoluteBoundingBox || { x: 0, y: 0, width: n.width || 0, height: n.height || 0 };
|
|
3974
|
+
const pbox = parent && parent.absoluteBoundingBox;
|
|
3975
|
+
const out = {
|
|
3976
|
+
id: n.id,
|
|
3977
|
+
name: n.name,
|
|
3978
|
+
type: n.type,
|
|
3979
|
+
x: pbox ? round(box.x - pbox.x) : 0,
|
|
3980
|
+
y: pbox ? round(box.y - pbox.y) : 0,
|
|
3981
|
+
w: round(box.width),
|
|
3982
|
+
h: round(box.height)
|
|
3983
|
+
};
|
|
3984
|
+
if (n.visible === false)
|
|
3985
|
+
out.hidden = true;
|
|
3986
|
+
if (typeof n.opacity === "number" && n.opacity < 1)
|
|
3987
|
+
out.opacity = round(n.opacity);
|
|
3988
|
+
if (n.rotation && Math.abs(n.rotation) > 0.01)
|
|
3989
|
+
out.rotation = round(-n.rotation);
|
|
3990
|
+
if (n.clipsContent)
|
|
3991
|
+
out.clip = true;
|
|
3992
|
+
if (n.constraints)
|
|
3993
|
+
out.constraints = { h: n.constraints.horizontal, v: n.constraints.vertical };
|
|
3994
|
+
if (n.layoutPositioning === "ABSOLUTE")
|
|
3995
|
+
out.absolute = true;
|
|
3996
|
+
if (n.layoutGrow === 1)
|
|
3997
|
+
out.grow = true;
|
|
3998
|
+
if (n.layoutAlign === "STRETCH")
|
|
3999
|
+
out.stretch = true;
|
|
4000
|
+
if ("layoutSizingHorizontal" in n)
|
|
4001
|
+
out.sizing = { h: n.layoutSizingHorizontal, v: n.layoutSizingVertical };
|
|
4002
|
+
const rb = n.absoluteRenderBounds;
|
|
4003
|
+
if (rb && (Math.abs(rb.x - box.x) > 0.5 || Math.abs(rb.y - box.y) > 0.5 || Math.abs(rb.width - box.width) > 0.5 || Math.abs(rb.height - box.height) > 0.5)) {
|
|
4004
|
+
out.render = { x: round(rb.x - box.x), y: round(rb.y - box.y), w: round(rb.width), h: round(rb.height) };
|
|
4005
|
+
}
|
|
4006
|
+
if ("fills" in n)
|
|
4007
|
+
out.fills = paints(n.fills);
|
|
4008
|
+
if ("strokes" in n && n.strokes.length) {
|
|
4009
|
+
out.strokes = paints(n.strokes);
|
|
4010
|
+
out.strokeWeight = n.strokeWeight === figma.mixed ? round(n.strokeTopWeight || 1) : round(n.strokeWeight);
|
|
4011
|
+
out.strokeAlign = n.strokeAlign;
|
|
4012
|
+
if (n.dashPattern && n.dashPattern.length)
|
|
4013
|
+
out.dashed = true;
|
|
4014
|
+
}
|
|
4015
|
+
if ("topLeftRadius" in n) {
|
|
4016
|
+
const r = [n.topLeftRadius, n.topRightRadius, n.bottomRightRadius, n.bottomLeftRadius].map(round);
|
|
4017
|
+
if (r[0] || r[1] || r[2] || r[3])
|
|
4018
|
+
out.radius = r;
|
|
4019
|
+
} else if (typeof n.cornerRadius === "number" && n.cornerRadius)
|
|
4020
|
+
out.radius = [round(n.cornerRadius), round(n.cornerRadius), round(n.cornerRadius), round(n.cornerRadius)];
|
|
4021
|
+
if ("effects" in n && n.effects.length) {
|
|
4022
|
+
out.effects = n.effects.filter(function(e) {
|
|
4023
|
+
return e.visible !== false;
|
|
4024
|
+
}).map(function(e) {
|
|
4025
|
+
return e.type === "DROP_SHADOW" || e.type === "INNER_SHADOW" ? { type: e.type, x: round(e.offset.x), y: round(e.offset.y), blur: round(e.radius), spread: round(e.spread || 0), color: toHex({ r: e.color.r, g: e.color.g, b: e.color.b }), alpha: round(e.color.a) } : { type: e.type, blur: round(e.radius) };
|
|
4026
|
+
});
|
|
4027
|
+
}
|
|
4028
|
+
if (n.reactions && n.reactions.length) {
|
|
4029
|
+
out.clickable = n.reactions.some(function(r) {
|
|
4030
|
+
return r.trigger && (r.trigger.type === "ON_CLICK" || r.trigger.type === "ON_PRESS");
|
|
4031
|
+
});
|
|
4032
|
+
}
|
|
4033
|
+
if (n.type !== "TEXT" && vectorOnly2(n))
|
|
4034
|
+
out.vector = true;
|
|
4035
|
+
const fills = out.fills || [];
|
|
4036
|
+
for (let i = 0;i < fills.length; i++) {
|
|
4037
|
+
const h = fills[i].imageHash;
|
|
4038
|
+
if (!h || ctx.images[h] !== undefined)
|
|
4039
|
+
continue;
|
|
4040
|
+
const image = figma.getImageByHash(h);
|
|
4041
|
+
if (!image)
|
|
4042
|
+
continue;
|
|
4043
|
+
const bytes = await image.getBytesAsync();
|
|
4044
|
+
if (ctx.imageBytes + bytes.length > 40 << 20)
|
|
4045
|
+
continue;
|
|
4046
|
+
ctx.imageBytes += bytes.length;
|
|
4047
|
+
ctx.images[h] = figma.base64Encode(bytes);
|
|
4048
|
+
}
|
|
4049
|
+
if (n.type === "TEXT") {
|
|
4050
|
+
out.text = textOf2(n);
|
|
4051
|
+
return out;
|
|
4052
|
+
}
|
|
4053
|
+
if (n.layoutMode && n.layoutMode !== "NONE") {
|
|
4054
|
+
const l = {
|
|
4055
|
+
mode: n.layoutMode,
|
|
4056
|
+
padding: [n.paddingTop, n.paddingRight, n.paddingBottom, n.paddingLeft].map(round),
|
|
4057
|
+
gap: round(n.itemSpacing || 0),
|
|
4058
|
+
primary: n.primaryAxisAlignItems,
|
|
4059
|
+
counter: n.counterAxisAlignItems
|
|
4060
|
+
};
|
|
4061
|
+
if (n.layoutWrap === "WRAP") {
|
|
4062
|
+
l.wrap = true;
|
|
4063
|
+
l.counterGap = round(n.counterAxisSpacing || 0);
|
|
4064
|
+
}
|
|
4065
|
+
if (n.layoutMode === "GRID") {
|
|
4066
|
+
l.columns = n.gridColumnCount;
|
|
4067
|
+
l.rows = n.gridRowCount;
|
|
4068
|
+
l.columnGap = round(n.gridColumnGap || 0);
|
|
4069
|
+
l.rowGap = round(n.gridRowGap || 0);
|
|
4070
|
+
}
|
|
4071
|
+
out.layout = l;
|
|
4072
|
+
}
|
|
4073
|
+
if ("children" in n && n.children.length && !out.vector) {
|
|
4074
|
+
out.children = [];
|
|
4075
|
+
for (let i = 0;i < n.children.length; i++) {
|
|
4076
|
+
if (ctx.count >= MAX_NODES5) {
|
|
4077
|
+
ctx.truncated = true;
|
|
4078
|
+
break;
|
|
4079
|
+
}
|
|
4080
|
+
out.children.push(await walk2(n.children[i], n, ctx));
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
return out;
|
|
4084
|
+
}
|
|
4085
|
+
function textOf2(t) {
|
|
4086
|
+
const segs = t.getStyledTextSegments(["fontName", "fontSize", "fontWeight", "fills", "textDecoration", "textCase", "letterSpacing", "lineHeight"]);
|
|
4087
|
+
return {
|
|
4088
|
+
characters: t.characters,
|
|
4089
|
+
align: t.textAlignHorizontal,
|
|
4090
|
+
valign: t.textAlignVertical,
|
|
4091
|
+
autoResize: t.textAutoResize,
|
|
4092
|
+
maxLines: t.textTruncation === "ENDING" ? t.maxLines || 1 : 0,
|
|
4093
|
+
segments: segs.map(function(s) {
|
|
4094
|
+
const fill = s.fills.filter(function(f) {
|
|
4095
|
+
return f.visible !== false;
|
|
4096
|
+
})[0];
|
|
4097
|
+
const seg = { text: s.characters, family: s.fontName.family, style: s.fontName.style, weight: s.fontWeight, size: round(s.fontSize) };
|
|
4098
|
+
if (fill && fill.type === "SOLID") {
|
|
4099
|
+
seg.color = toHex(fill.color);
|
|
4100
|
+
if (fill.opacity !== undefined && fill.opacity < 1)
|
|
4101
|
+
seg.alpha = round(fill.opacity);
|
|
4102
|
+
}
|
|
4103
|
+
if (s.textDecoration !== "NONE")
|
|
4104
|
+
seg.decoration = s.textDecoration;
|
|
4105
|
+
if (s.textCase && s.textCase !== "ORIGINAL")
|
|
4106
|
+
seg.textCase = s.textCase;
|
|
4107
|
+
if (s.letterSpacing && s.letterSpacing.value)
|
|
4108
|
+
seg.letterSpacing = round(s.letterSpacing.unit === "PERCENT" ? s.letterSpacing.value / 100 * s.fontSize : s.letterSpacing.value);
|
|
4109
|
+
if (s.lineHeight && s.lineHeight.unit !== "AUTO")
|
|
4110
|
+
seg.lineHeight = round(s.lineHeight.unit === "PERCENT" ? s.lineHeight.value / 100 * s.fontSize : s.lineHeight.value);
|
|
4111
|
+
return seg;
|
|
4112
|
+
})
|
|
4113
|
+
};
|
|
4114
|
+
}
|
|
4115
|
+
async function robloxImages(p) {
|
|
4116
|
+
const items = Array.isArray(p.items) ? p.items : [];
|
|
4117
|
+
const out = [];
|
|
4118
|
+
for (let i = 0;i < items.length; i++) {
|
|
4119
|
+
const it = items[i];
|
|
4120
|
+
const scale = Math.max(1, Math.min(4, Number(it.scale) || 2));
|
|
4121
|
+
let temp = null;
|
|
4122
|
+
try {
|
|
4123
|
+
let node = await getNode(it.id);
|
|
4124
|
+
if (it.mode === "panel" || it.mode === "shadow") {
|
|
4125
|
+
temp = node.clone();
|
|
4126
|
+
if ("children" in temp)
|
|
4127
|
+
for (let k = temp.children.length - 1;k >= 0; k--)
|
|
4128
|
+
temp.children[k].remove();
|
|
4129
|
+
if (it.mode === "panel")
|
|
4130
|
+
temp.effects = [];
|
|
4131
|
+
else {
|
|
4132
|
+
temp.effects = node.effects.filter(function(e) {
|
|
4133
|
+
return e.type === "DROP_SHADOW" && e.visible !== false;
|
|
4134
|
+
});
|
|
4135
|
+
temp.strokes = [];
|
|
4136
|
+
}
|
|
4137
|
+
node = temp;
|
|
4138
|
+
}
|
|
4139
|
+
const bytes = await node.exportAsync({ format: "PNG", constraint: { type: "SCALE", value: scale } });
|
|
4140
|
+
const rb = node.absoluteRenderBounds || node.absoluteBoundingBox;
|
|
4141
|
+
const bb = node.absoluteBoundingBox;
|
|
4142
|
+
out.push({
|
|
4143
|
+
id: it.id,
|
|
4144
|
+
mode: it.mode || "full",
|
|
4145
|
+
scale,
|
|
4146
|
+
b64: figma.base64Encode(bytes),
|
|
4147
|
+
offset: rb && bb ? { x: round(rb.x - bb.x), y: round(rb.y - bb.y) } : { x: 0, y: 0 },
|
|
4148
|
+
size: rb ? { w: round(rb.width), h: round(rb.height) } : { w: round(node.width), h: round(node.height) }
|
|
4149
|
+
});
|
|
4150
|
+
} catch (e) {
|
|
4151
|
+
out.push({ id: it.id, mode: it.mode || "full", error: String(e.message || e) });
|
|
4152
|
+
} finally {
|
|
4153
|
+
if (temp)
|
|
4154
|
+
temp.remove();
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
return { images: out };
|
|
4158
|
+
}
|
|
4159
|
+
|
|
3642
4160
|
// plugin/lib/selection.ts
|
|
3643
4161
|
var MAX_WAIT_MS = 120000;
|
|
3644
4162
|
var waiters = {};
|
|
@@ -3708,7 +4226,7 @@
|
|
|
3708
4226
|
finish(id, codeError("The user cancelled the selection request", "CANCELLED"));
|
|
3709
4227
|
}
|
|
3710
4228
|
// package.json
|
|
3711
|
-
var version = "1.
|
|
4229
|
+
var version = "1.13.0";
|
|
3712
4230
|
|
|
3713
4231
|
// plugin/code.ts
|
|
3714
4232
|
var DEFAULT_SIZE = { width: 340, height: 540 };
|
|
@@ -3730,7 +4248,11 @@
|
|
|
3730
4248
|
figma.ui.postMessage(msg);
|
|
3731
4249
|
}
|
|
3732
4250
|
function sessionInfo() {
|
|
3733
|
-
|
|
4251
|
+
let fileKey;
|
|
4252
|
+
try {
|
|
4253
|
+
fileKey = figma.fileKey;
|
|
4254
|
+
} catch (e) {}
|
|
4255
|
+
return { id: sessionId, fileName: figma.root.name, page: figma.currentPage.name, pageId: figma.currentPage.id, editorType: figma.editorType, fileKey };
|
|
3734
4256
|
}
|
|
3735
4257
|
function clampSize(w, h) {
|
|
3736
4258
|
return {
|
|
@@ -3787,6 +4309,9 @@
|
|
|
3787
4309
|
size = { width: DEFAULT_SIZE.width, height: DEFAULT_SIZE.height };
|
|
3788
4310
|
applySize2();
|
|
3789
4311
|
figma.clientStorage.setAsync("size", size);
|
|
4312
|
+
} else if (msg.t === "reopen") {
|
|
4313
|
+
figma.notify("Figma Bridge updated: run it again (Ctrl+Alt+P)", { timeout: 6000 });
|
|
4314
|
+
figma.closePlugin();
|
|
3790
4315
|
} else if (msg.t === "notify")
|
|
3791
4316
|
figma.notify(String(msg.text), { timeout: 2500 });
|
|
3792
4317
|
else if (msg.t === "focus")
|
|
@@ -3832,10 +4357,20 @@
|
|
|
3832
4357
|
prototype,
|
|
3833
4358
|
annotate,
|
|
3834
4359
|
export_tree: exportTree,
|
|
4360
|
+
node_info: nodeInfo,
|
|
4361
|
+
roblox_tree: robloxTree,
|
|
4362
|
+
roblox_images: robloxImages,
|
|
3835
4363
|
ping: function() {
|
|
3836
4364
|
return Promise.resolve({ pong: true, session: sessionInfo() });
|
|
3837
4365
|
}
|
|
3838
4366
|
};
|
|
4367
|
+
var EDITORS2 = {
|
|
4368
|
+
prototype: ["figma"],
|
|
4369
|
+
annotate: ["figma", "dev"],
|
|
4370
|
+
design_tokens: ["figma", "slides"],
|
|
4371
|
+
export_tokens: ["figma", "slides", "dev"]
|
|
4372
|
+
};
|
|
4373
|
+
var EDITOR_NAMES2 = { figma: "Figma Design", figjam: "FigJam", slides: "Figma Slides", dev: "Dev Mode" };
|
|
3839
4374
|
var READ_ONLY = {
|
|
3840
4375
|
describe: true,
|
|
3841
4376
|
find: true,
|
|
@@ -3848,6 +4383,8 @@
|
|
|
3848
4383
|
list_fonts: true,
|
|
3849
4384
|
wait_for_selection: true,
|
|
3850
4385
|
export_tree: true,
|
|
4386
|
+
node_info: true,
|
|
4387
|
+
roblox_tree: true,
|
|
3851
4388
|
ping: true
|
|
3852
4389
|
};
|
|
3853
4390
|
async function handleRequest(msg) {
|
|
@@ -3860,6 +4397,14 @@
|
|
|
3860
4397
|
const handler = HANDLERS[msg.method];
|
|
3861
4398
|
if (!handler)
|
|
3862
4399
|
throw codeError("Unknown method: " + msg.method + " (reopen the plugin after updating it)", "UNKNOWN_METHOD");
|
|
4400
|
+
const editors = EDITORS2[msg.method];
|
|
4401
|
+
if (editors && editors.indexOf(figma.editorType) === -1) {
|
|
4402
|
+
throw codeError(msg.method + " is not available in " + (EDITOR_NAMES2[figma.editorType] || figma.editorType) + " (works in " + editors.map(function(e) {
|
|
4403
|
+
return EDITOR_NAMES2[e];
|
|
4404
|
+
}).join(", ") + ").", "WRONG_EDITOR");
|
|
4405
|
+
}
|
|
4406
|
+
if (mutates && figma.editorType === "dev")
|
|
4407
|
+
throw codeError("Dev Mode is read-only: switch the file to Design mode to let the agent edit it.", "WRONG_EDITOR");
|
|
3863
4408
|
const result = await handler(msg.params || {}, Math.min(Number(msg.timeoutMs) || 30000, 120000), String(msg.id));
|
|
3864
4409
|
reply = { t: "res", id: msg.id, ok: true, result };
|
|
3865
4410
|
} catch (e) {
|
|
@@ -4161,6 +4706,18 @@
|
|
|
4161
4706
|
}
|
|
4162
4707
|
};
|
|
4163
4708
|
}
|
|
4709
|
+
async function nodeInfo(p) {
|
|
4710
|
+
const ids = Array.isArray(p.ids) ? p.ids.slice(0, 500) : [];
|
|
4711
|
+
const out = {};
|
|
4712
|
+
for (let i = 0;i < ids.length; i++) {
|
|
4713
|
+
const n = await figma.getNodeByIdAsync(String(ids[i]));
|
|
4714
|
+
if (!n)
|
|
4715
|
+
continue;
|
|
4716
|
+
const pg = pageOf(n);
|
|
4717
|
+
out[ids[i]] = { name: n.name, type: n.type, page: pg ? pg.name : undefined };
|
|
4718
|
+
}
|
|
4719
|
+
return out;
|
|
4720
|
+
}
|
|
4164
4721
|
var fontCache = null;
|
|
4165
4722
|
async function listFonts(p) {
|
|
4166
4723
|
if (!fontCache)
|