@mico_fe/ftc-plugin 0.2.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 +159 -0
- package/dist/code.js +3784 -0
- package/dist/index.html +380 -0
- package/install.sh +184 -0
- package/manifest.json +21 -0
- package/package.json +58 -0
- package/uninstall.sh +114 -0
package/dist/code.js
ADDED
|
@@ -0,0 +1,3784 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
5
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
8
|
+
var __pow = Math.pow;
|
|
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 __objRest = (source, exclude) => {
|
|
23
|
+
var target = {};
|
|
24
|
+
for (var prop in source)
|
|
25
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
26
|
+
target[prop] = source[prop];
|
|
27
|
+
if (source != null && __getOwnPropSymbols)
|
|
28
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
29
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
30
|
+
target[prop] = source[prop];
|
|
31
|
+
}
|
|
32
|
+
return target;
|
|
33
|
+
};
|
|
34
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
35
|
+
var __async = (__this, __arguments, generator) => {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
var fulfilled = (value) => {
|
|
38
|
+
try {
|
|
39
|
+
step(generator.next(value));
|
|
40
|
+
} catch (e) {
|
|
41
|
+
reject(e);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var rejected = (value) => {
|
|
45
|
+
try {
|
|
46
|
+
step(generator.throw(value));
|
|
47
|
+
} catch (e) {
|
|
48
|
+
reject(e);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
52
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// ../../packages/pluginMain/src/common/indentString.ts
|
|
57
|
+
var indentString = (str, indentLevel = 2) => {
|
|
58
|
+
const regex = /^(?!\s*$)/gm;
|
|
59
|
+
return str.replace(regex, " ".repeat(indentLevel));
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ../../packages/types/src/types.ts
|
|
63
|
+
var DEFAULT_LAYOUT_UNIT = "rem-100";
|
|
64
|
+
var LAYOUT_UNIT_STORAGE_KEY = "layout-unit";
|
|
65
|
+
|
|
66
|
+
// ../../packages/pluginMain/src/clientStorage.ts
|
|
67
|
+
var PREFIX = "code-forge:";
|
|
68
|
+
var setClientStorage = (key, value, prefix = "") => __async(null, null, function* () {
|
|
69
|
+
try {
|
|
70
|
+
yield figma.clientStorage.setAsync(`${prefix}${key}`, value);
|
|
71
|
+
console.log(`[ClientStorage] Set key "${prefix}${key}":`, value);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(`[ClientStorage] Failed to set key "${prefix}${key}":`, error);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
var getClientStorage = (key, prefix = "") => __async(null, null, function* () {
|
|
78
|
+
try {
|
|
79
|
+
const value = yield figma.clientStorage.getAsync(`${prefix}${key}`);
|
|
80
|
+
console.log(`[ClientStorage] Get key "${prefix}${key}":`, value);
|
|
81
|
+
return value !== void 0 ? value : null;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`[ClientStorage] Failed to get key "${prefix}${key}":`, error);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
var getClientStorageWithDefault = (key, defaultValue, prefix = "") => __async(null, null, function* () {
|
|
88
|
+
const value = yield getClientStorage(key, prefix);
|
|
89
|
+
return value !== null ? value : defaultValue;
|
|
90
|
+
});
|
|
91
|
+
var removeClientStorage = (key, prefix = "") => __async(null, null, function* () {
|
|
92
|
+
try {
|
|
93
|
+
yield figma.clientStorage.deleteAsync(`${prefix}${key}`);
|
|
94
|
+
console.log(`[ClientStorage] Removed key "${prefix}${key}"`);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(`[ClientStorage] Failed to remove key "${prefix}${key}":`, error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
var getAllClientStorageKeys = () => __async(null, null, function* () {
|
|
101
|
+
try {
|
|
102
|
+
const keys = yield figma.clientStorage.keysAsync();
|
|
103
|
+
console.log(`[ClientStorage] All keys:`, keys);
|
|
104
|
+
return keys;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(`[ClientStorage] Failed to get all keys:`, error);
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
var handleClientStorageMessage = (msg) => __async(null, null, function* () {
|
|
111
|
+
const { action, key, value } = msg;
|
|
112
|
+
try {
|
|
113
|
+
switch (action) {
|
|
114
|
+
case "set":
|
|
115
|
+
if (key !== void 0) {
|
|
116
|
+
yield setClientStorage(key, value);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case "remove":
|
|
120
|
+
if (key !== void 0) {
|
|
121
|
+
yield removeClientStorage(key);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
case "clear":
|
|
125
|
+
const allKeys = yield getAllClientStorageKeys();
|
|
126
|
+
const keysToDelete = allKeys.filter((k) => k.startsWith(PREFIX));
|
|
127
|
+
yield Promise.all(keysToDelete.map((k) => removeClientStorage(k)));
|
|
128
|
+
console.log(`[ClientStorage] Cleared ${keysToDelete.length} keys`);
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
console.warn(`[ClientStorage] Unknown action: ${String(action)}`);
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
console.error(`[ClientStorage] Error handling message:`, error);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// ../../packages/pluginMain/src/common/numToAutoFixed.ts
|
|
139
|
+
var numberToFixedString = (num) => {
|
|
140
|
+
return num.toFixed(2).replace(/\.00$/, "");
|
|
141
|
+
};
|
|
142
|
+
var roundToNearestDecimal = (decimal) => (n) => Math.round(n * __pow(10, decimal)) / __pow(10, decimal);
|
|
143
|
+
var roundToNearestHundreth = roundToNearestDecimal(2);
|
|
144
|
+
function stringToClassName(name) {
|
|
145
|
+
const words = name.split(/[^a-zA-Z0-9]+/);
|
|
146
|
+
const camelCaseWords = words.map((word, index) => {
|
|
147
|
+
if (index === 0) {
|
|
148
|
+
const cleanedWord = word.replace(/^[^a-zA-Z]+/g, "");
|
|
149
|
+
return cleanedWord.charAt(0).toUpperCase() + cleanedWord.slice(1).toLowerCase();
|
|
150
|
+
}
|
|
151
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
152
|
+
});
|
|
153
|
+
return camelCaseWords.join("");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ../../packages/pluginMain/src/common/layoutUnit.ts
|
|
157
|
+
var currentLayoutUnit = DEFAULT_LAYOUT_UNIT;
|
|
158
|
+
var setCurrentLayoutUnit = (unit) => {
|
|
159
|
+
currentLayoutUnit = unit;
|
|
160
|
+
};
|
|
161
|
+
var formatSizeValue = (num) => {
|
|
162
|
+
switch (currentLayoutUnit) {
|
|
163
|
+
case "rem-100":
|
|
164
|
+
return `${numberToFixedString(num / 100)}rem`;
|
|
165
|
+
case "rem-50":
|
|
166
|
+
return `${numberToFixedString(num / 50)}rem`;
|
|
167
|
+
default:
|
|
168
|
+
return `${numberToFixedString(num)}px`;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
var initLayoutUnitFromStorage = () => __async(null, null, function* () {
|
|
172
|
+
const unit = yield getClientStorageWithDefault(
|
|
173
|
+
LAYOUT_UNIT_STORAGE_KEY,
|
|
174
|
+
DEFAULT_LAYOUT_UNIT,
|
|
175
|
+
PREFIX
|
|
176
|
+
);
|
|
177
|
+
setCurrentLayoutUnit(unit);
|
|
178
|
+
return unit;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ../../packages/pluginMain/src/common/parseJSX.ts
|
|
182
|
+
var formatWithJSX = (property, isJsx, value) => {
|
|
183
|
+
const jsx_property = property.split("-").map((d, i) => i > 0 ? d.charAt(0).toUpperCase() + d.slice(1) : d).join("");
|
|
184
|
+
if (typeof value === "number") {
|
|
185
|
+
if (isJsx) {
|
|
186
|
+
return `${jsx_property}: ${numberToFixedString(value)}`;
|
|
187
|
+
} else {
|
|
188
|
+
return `${property}: ${formatSizeValue(value)}`;
|
|
189
|
+
}
|
|
190
|
+
} else if (isJsx) {
|
|
191
|
+
return `${jsx_property}: '${value}'`;
|
|
192
|
+
} else {
|
|
193
|
+
return `${property}: ${value}`;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
var formatMultipleJSXArray = (styles, isJsx) => Object.entries(styles).filter(([, value]) => value !== "").map(([key, value]) => formatWithJSX(key, isJsx, value));
|
|
197
|
+
var formatMultipleJSX = (styles, isJsx) => Object.entries(styles).filter(([, value]) => value).map(([key, value]) => formatWithJSX(key, isJsx, value)).join(isJsx ? ", " : "; ");
|
|
198
|
+
|
|
199
|
+
// ../../packages/pluginMain/src/common/retrieveFill.ts
|
|
200
|
+
var retrieveTopFill = (fills) => {
|
|
201
|
+
if (fills && Array.isArray(fills) && fills.length > 0) {
|
|
202
|
+
return [...fills].reverse().find((d) => d.visible !== false);
|
|
203
|
+
}
|
|
204
|
+
return void 0;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlColor.ts
|
|
208
|
+
var processColorWithVariable = (fill) => {
|
|
209
|
+
var _a;
|
|
210
|
+
const opacity = (_a = fill.opacity) != null ? _a : 1;
|
|
211
|
+
if (fill.variableColorName) {
|
|
212
|
+
const varName = fill.variableColorName;
|
|
213
|
+
const fallbackColor = htmlColor(fill.color, opacity);
|
|
214
|
+
return `var(--${varName}, ${fallbackColor})`;
|
|
215
|
+
}
|
|
216
|
+
return htmlColor(fill.color, opacity);
|
|
217
|
+
};
|
|
218
|
+
var getColorAndVariable = (fill) => {
|
|
219
|
+
var _a, _b;
|
|
220
|
+
if (fill.type === "SOLID") {
|
|
221
|
+
return {
|
|
222
|
+
color: fill.color,
|
|
223
|
+
opacity: (_a = fill.opacity) != null ? _a : 1,
|
|
224
|
+
variableColorName: fill.variableColorName
|
|
225
|
+
};
|
|
226
|
+
} else if ((fill.type === "GRADIENT_LINEAR" || fill.type === "GRADIENT_RADIAL" || fill.type === "GRADIENT_ANGULAR" || fill.type === "GRADIENT_DIAMOND") && fill.gradientStops.length > 0) {
|
|
227
|
+
const firstStop = fill.gradientStops[0];
|
|
228
|
+
if (!firstStop) {
|
|
229
|
+
return { color: { r: 0, g: 0, b: 0 }, opacity: 0 };
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
color: firstStop.color,
|
|
233
|
+
opacity: (_b = fill.opacity) != null ? _b : 1,
|
|
234
|
+
variableColorName: firstStop.variableColorName
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return { color: { r: 0, g: 0, b: 0 }, opacity: 0 };
|
|
238
|
+
};
|
|
239
|
+
var htmlColorFromFills = (fills) => {
|
|
240
|
+
const fill = retrieveTopFill(fills);
|
|
241
|
+
if (fill) {
|
|
242
|
+
const colorInfo = getColorAndVariable(fill);
|
|
243
|
+
return processColorWithVariable(colorInfo);
|
|
244
|
+
}
|
|
245
|
+
return "";
|
|
246
|
+
};
|
|
247
|
+
var htmlColorFromFill = (fill) => {
|
|
248
|
+
return processColorWithVariable(fill);
|
|
249
|
+
};
|
|
250
|
+
var htmlColor = (color, alpha = 1) => {
|
|
251
|
+
if (color.r === 1 && color.g === 1 && color.b === 1 && alpha === 1) {
|
|
252
|
+
return "white";
|
|
253
|
+
}
|
|
254
|
+
if (color.r === 0 && color.g === 0 && color.b === 0 && alpha === 1) {
|
|
255
|
+
return "black";
|
|
256
|
+
}
|
|
257
|
+
if (alpha === 1) {
|
|
258
|
+
const r2 = Math.round(color.r * 255);
|
|
259
|
+
const g2 = Math.round(color.g * 255);
|
|
260
|
+
const b2 = Math.round(color.b * 255);
|
|
261
|
+
const toHex = (num) => num.toString(16).padStart(2, "0");
|
|
262
|
+
return `#${toHex(r2)}${toHex(g2)}${toHex(b2)}`.toUpperCase();
|
|
263
|
+
}
|
|
264
|
+
const r = numberToFixedString(color.r * 255);
|
|
265
|
+
const g = numberToFixedString(color.g * 255);
|
|
266
|
+
const b = numberToFixedString(color.b * 255);
|
|
267
|
+
const a = numberToFixedString(alpha);
|
|
268
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
269
|
+
};
|
|
270
|
+
var processGradientStop = (stop, fillOpacity = 1, positionMultiplier = 100, unit = "%") => {
|
|
271
|
+
const fillInfo = {
|
|
272
|
+
color: stop.color,
|
|
273
|
+
opacity: stop.color.a * fillOpacity,
|
|
274
|
+
boundVariables: stop.boundVariables,
|
|
275
|
+
variableColorName: stop.variableColorName
|
|
276
|
+
};
|
|
277
|
+
const color = processColorWithVariable(fillInfo);
|
|
278
|
+
const position = `${(stop.position * positionMultiplier).toFixed(0)}${unit}`;
|
|
279
|
+
return `${color} ${position}`;
|
|
280
|
+
};
|
|
281
|
+
var processGradientStops = (stops, fillOpacity = 1, positionMultiplier = 100, unit = "%") => {
|
|
282
|
+
return stops.map((stop) => processGradientStop(stop, fillOpacity, positionMultiplier, unit)).join(", ");
|
|
283
|
+
};
|
|
284
|
+
var htmlGradientFromFills = (fill) => {
|
|
285
|
+
if (!fill) return "";
|
|
286
|
+
switch (fill.type) {
|
|
287
|
+
case "GRADIENT_LINEAR":
|
|
288
|
+
return htmlLinearGradient(fill);
|
|
289
|
+
case "GRADIENT_ANGULAR":
|
|
290
|
+
return htmlAngularGradient(fill);
|
|
291
|
+
case "GRADIENT_RADIAL":
|
|
292
|
+
return htmlRadialGradient(fill);
|
|
293
|
+
case "GRADIENT_DIAMOND":
|
|
294
|
+
return htmlDiamondGradient(fill);
|
|
295
|
+
default:
|
|
296
|
+
return "";
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
var htmlLinearGradient = (fill) => {
|
|
300
|
+
var _a;
|
|
301
|
+
const [start, end] = fill.gradientHandlePositions;
|
|
302
|
+
if (!start || !end) return "";
|
|
303
|
+
const dx = end.x - start.x;
|
|
304
|
+
const dy = end.y - start.y;
|
|
305
|
+
let angle = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
306
|
+
angle = (angle + 360) % 360;
|
|
307
|
+
const cssAngle = (angle + 90) % 360;
|
|
308
|
+
const mappedFill = processGradientStops(fill.gradientStops, (_a = fill.opacity) != null ? _a : 1);
|
|
309
|
+
return `linear-gradient(${cssAngle.toFixed(0)}deg, ${mappedFill})`;
|
|
310
|
+
};
|
|
311
|
+
var htmlRadialGradient = (fill) => {
|
|
312
|
+
var _a;
|
|
313
|
+
const [center, h1, h2] = fill.gradientHandlePositions;
|
|
314
|
+
if (!center || !h1 || !h2) return "";
|
|
315
|
+
const cx = center.x * 100;
|
|
316
|
+
const cy = center.y * 100;
|
|
317
|
+
const rx = Math.sqrt(__pow(h1.x - center.x, 2) + __pow(h1.y - center.y, 2)) * 100;
|
|
318
|
+
const ry = Math.sqrt(__pow(h2.x - center.x, 2) + __pow(h2.y - center.y, 2)) * 100;
|
|
319
|
+
const mappedStops = processGradientStops(fill.gradientStops, (_a = fill.opacity) != null ? _a : 1);
|
|
320
|
+
return `radial-gradient(ellipse ${rx.toFixed(2)}% ${ry.toFixed(2)}% at ${cx.toFixed(2)}% ${cy.toFixed(2)}%, ${mappedStops})`;
|
|
321
|
+
};
|
|
322
|
+
var htmlAngularGradient = (fill) => {
|
|
323
|
+
var _a;
|
|
324
|
+
const [center, _, startDirection] = fill.gradientHandlePositions;
|
|
325
|
+
if (!center || !startDirection) return "";
|
|
326
|
+
const cx = center.x * 100;
|
|
327
|
+
const cy = center.y * 100;
|
|
328
|
+
const dx = startDirection.x - center.x;
|
|
329
|
+
const dy = startDirection.y - center.y;
|
|
330
|
+
let angle = Math.atan2(dy, dx) * (180 / Math.PI);
|
|
331
|
+
angle = (angle + 360) % 360;
|
|
332
|
+
const mappedFill = processGradientStops(fill.gradientStops, (_a = fill.opacity) != null ? _a : 1, 360, "deg");
|
|
333
|
+
return `conic-gradient(from ${angle.toFixed(0)}deg at ${cx.toFixed(2)}% ${cy.toFixed(2)}%, ${mappedFill})`;
|
|
334
|
+
};
|
|
335
|
+
var htmlDiamondGradient = (fill) => {
|
|
336
|
+
var _a;
|
|
337
|
+
const stops = processGradientStops(fill.gradientStops, (_a = fill.opacity) != null ? _a : 1, 50, "%");
|
|
338
|
+
const gradientConfigs = [
|
|
339
|
+
{ direction: "to bottom right", position: "bottom right" },
|
|
340
|
+
{ direction: "to bottom left", position: "bottom left" },
|
|
341
|
+
{ direction: "to top left", position: "top left" },
|
|
342
|
+
{ direction: "to top right", position: "top right" }
|
|
343
|
+
];
|
|
344
|
+
return gradientConfigs.map(
|
|
345
|
+
({ direction, position }) => `linear-gradient(${direction}, ${stops}) ${position} / 50% 50% no-repeat`
|
|
346
|
+
).join(", ");
|
|
347
|
+
};
|
|
348
|
+
var buildBackgroundValues = (paintArray) => {
|
|
349
|
+
if (paintArray === figma.mixed) {
|
|
350
|
+
return "";
|
|
351
|
+
}
|
|
352
|
+
if (paintArray.length === 1) {
|
|
353
|
+
const paint = paintArray[0];
|
|
354
|
+
if (!paint) return "";
|
|
355
|
+
if (paint.type === "SOLID") {
|
|
356
|
+
return htmlColorFromFills(paintArray);
|
|
357
|
+
} else if (paint.type === "GRADIENT_LINEAR" || paint.type === "GRADIENT_RADIAL" || paint.type === "GRADIENT_ANGULAR" || paint.type === "GRADIENT_DIAMOND") {
|
|
358
|
+
return htmlGradientFromFills(paint);
|
|
359
|
+
}
|
|
360
|
+
return "";
|
|
361
|
+
}
|
|
362
|
+
const styles = [...paintArray].reverse().map((paint, index) => {
|
|
363
|
+
if (paint.type === "SOLID") {
|
|
364
|
+
const color = htmlColorFromFills([paint]);
|
|
365
|
+
if (index === 0) {
|
|
366
|
+
return `linear-gradient(0deg, ${color} 0%, ${color} 100%)`;
|
|
367
|
+
}
|
|
368
|
+
return color;
|
|
369
|
+
} else if (paint.type === "GRADIENT_LINEAR" || paint.type === "GRADIENT_RADIAL" || paint.type === "GRADIENT_ANGULAR" || paint.type === "GRADIENT_DIAMOND") {
|
|
370
|
+
return htmlGradientFromFills(paint);
|
|
371
|
+
}
|
|
372
|
+
return "";
|
|
373
|
+
});
|
|
374
|
+
return styles.filter((value) => value !== "").join(", ");
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlShadow.ts
|
|
378
|
+
var isShadowOrBlurEffect = (effect) => (effect.type === "DROP_SHADOW" || effect.type === "INNER_SHADOW" || effect.type === "LAYER_BLUR") && effect.visible;
|
|
379
|
+
var htmlShadow = (node) => {
|
|
380
|
+
if (node.effects && node.effects.length > 0) {
|
|
381
|
+
const shadowEffects = node.effects.filter(isShadowOrBlurEffect);
|
|
382
|
+
if (shadowEffects.length > 0) {
|
|
383
|
+
const shadow = shadowEffects[0];
|
|
384
|
+
let x = 0;
|
|
385
|
+
let y = 0;
|
|
386
|
+
let blur = 0;
|
|
387
|
+
let spread = "";
|
|
388
|
+
let inner = "";
|
|
389
|
+
let color = "";
|
|
390
|
+
if (shadow.type === "DROP_SHADOW" || shadow.type === "INNER_SHADOW") {
|
|
391
|
+
x = shadow.offset.x;
|
|
392
|
+
y = shadow.offset.y;
|
|
393
|
+
blur = shadow.radius;
|
|
394
|
+
spread = shadow.spread ? `${formatSizeValue(shadow.spread)} ` : "";
|
|
395
|
+
inner = shadow.type === "INNER_SHADOW" ? " inset" : "";
|
|
396
|
+
color = htmlColor(shadow.color, shadow.color.a);
|
|
397
|
+
} else if (shadow.type === "LAYER_BLUR") {
|
|
398
|
+
x = shadow.radius;
|
|
399
|
+
y = shadow.radius;
|
|
400
|
+
blur = shadow.radius;
|
|
401
|
+
}
|
|
402
|
+
return `${formatSizeValue(x)} ${formatSizeValue(y)} ${formatSizeValue(blur)} ${spread}${color}${inner}`;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return "";
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlBlend.ts
|
|
409
|
+
var htmlOpacity = (node, isJsx) => {
|
|
410
|
+
if (node.opacity !== void 0 && node.opacity !== 1) {
|
|
411
|
+
if (isJsx) {
|
|
412
|
+
return `opacity: ${numberToFixedString(node.opacity)}`;
|
|
413
|
+
} else {
|
|
414
|
+
return `opacity: ${numberToFixedString(node.opacity)}`;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return "";
|
|
418
|
+
};
|
|
419
|
+
var htmlBlendMode = (node, isJsx) => {
|
|
420
|
+
if (node.blendMode !== "NORMAL" && node.blendMode !== "PASS_THROUGH") {
|
|
421
|
+
let blendMode = "";
|
|
422
|
+
switch (node.blendMode) {
|
|
423
|
+
case "MULTIPLY":
|
|
424
|
+
blendMode = "multiply";
|
|
425
|
+
break;
|
|
426
|
+
case "SCREEN":
|
|
427
|
+
blendMode = "screen";
|
|
428
|
+
break;
|
|
429
|
+
case "OVERLAY":
|
|
430
|
+
blendMode = "overlay";
|
|
431
|
+
break;
|
|
432
|
+
case "DARKEN":
|
|
433
|
+
blendMode = "darken";
|
|
434
|
+
break;
|
|
435
|
+
case "LIGHTEN":
|
|
436
|
+
blendMode = "lighten";
|
|
437
|
+
break;
|
|
438
|
+
case "COLOR_DODGE":
|
|
439
|
+
blendMode = "color-dodge";
|
|
440
|
+
break;
|
|
441
|
+
case "COLOR_BURN":
|
|
442
|
+
blendMode = "color-burn";
|
|
443
|
+
break;
|
|
444
|
+
case "HARD_LIGHT":
|
|
445
|
+
blendMode = "hard-light";
|
|
446
|
+
break;
|
|
447
|
+
case "SOFT_LIGHT":
|
|
448
|
+
blendMode = "soft-light";
|
|
449
|
+
break;
|
|
450
|
+
case "DIFFERENCE":
|
|
451
|
+
blendMode = "difference";
|
|
452
|
+
break;
|
|
453
|
+
case "EXCLUSION":
|
|
454
|
+
blendMode = "exclusion";
|
|
455
|
+
break;
|
|
456
|
+
case "HUE":
|
|
457
|
+
blendMode = "hue";
|
|
458
|
+
break;
|
|
459
|
+
case "SATURATION":
|
|
460
|
+
blendMode = "saturation";
|
|
461
|
+
break;
|
|
462
|
+
case "COLOR":
|
|
463
|
+
blendMode = "color";
|
|
464
|
+
break;
|
|
465
|
+
case "LUMINOSITY":
|
|
466
|
+
blendMode = "luminosity";
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
if (blendMode) {
|
|
470
|
+
return formatWithJSX("mix-blend-mode", isJsx, blendMode);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return "";
|
|
474
|
+
};
|
|
475
|
+
var htmlVisibility = (node, isJsx) => {
|
|
476
|
+
if (node.visible !== void 0 && !node.visible) {
|
|
477
|
+
return formatWithJSX("visibility", isJsx, "hidden");
|
|
478
|
+
}
|
|
479
|
+
return "";
|
|
480
|
+
};
|
|
481
|
+
var htmlRotation = (node, isJsx) => {
|
|
482
|
+
const rotation = -Math.round((node.rotation || 0) + (node.cumulativeRotation || 0)) || 0;
|
|
483
|
+
if (rotation !== 0) {
|
|
484
|
+
return [
|
|
485
|
+
formatWithJSX("transform", isJsx, `rotate(${numberToFixedString(rotation)}deg)`),
|
|
486
|
+
formatWithJSX("transform-origin", isJsx, "top left")
|
|
487
|
+
];
|
|
488
|
+
}
|
|
489
|
+
return [];
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// ../../packages/pluginMain/src/common/commonPadding.ts
|
|
493
|
+
var commonPadding = (node) => {
|
|
494
|
+
var _a, _b, _c, _d;
|
|
495
|
+
if ("layoutMode" in node && node.layoutMode !== "NONE") {
|
|
496
|
+
const paddingLeft = parseFloat(((_a = node.paddingLeft) != null ? _a : 0).toFixed(2));
|
|
497
|
+
const paddingRight = parseFloat(((_b = node.paddingRight) != null ? _b : 0).toFixed(2));
|
|
498
|
+
const paddingTop = parseFloat(((_c = node.paddingTop) != null ? _c : 0).toFixed(2));
|
|
499
|
+
const paddingBottom = parseFloat(((_d = node.paddingBottom) != null ? _d : 0).toFixed(2));
|
|
500
|
+
if (paddingLeft === paddingRight && paddingLeft === paddingBottom && paddingTop === paddingBottom) {
|
|
501
|
+
return { all: paddingLeft };
|
|
502
|
+
} else if (paddingLeft === paddingRight && paddingTop === paddingBottom) {
|
|
503
|
+
return {
|
|
504
|
+
horizontal: paddingLeft,
|
|
505
|
+
vertical: paddingTop
|
|
506
|
+
};
|
|
507
|
+
} else {
|
|
508
|
+
return {
|
|
509
|
+
left: paddingLeft,
|
|
510
|
+
right: paddingRight,
|
|
511
|
+
top: paddingTop,
|
|
512
|
+
bottom: paddingBottom
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return null;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlPadding.ts
|
|
520
|
+
var htmlPadding = (node, isJsx) => {
|
|
521
|
+
const padding = commonPadding(node);
|
|
522
|
+
if (padding === null) {
|
|
523
|
+
return [];
|
|
524
|
+
}
|
|
525
|
+
if ("all" in padding) {
|
|
526
|
+
if (padding.all !== 0) {
|
|
527
|
+
return [formatWithJSX("padding", isJsx, padding.all)];
|
|
528
|
+
} else {
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const comp = [];
|
|
533
|
+
if ("horizontal" in padding) {
|
|
534
|
+
if (padding.horizontal !== 0) {
|
|
535
|
+
comp.push(formatWithJSX("padding-left", isJsx, padding.horizontal));
|
|
536
|
+
comp.push(formatWithJSX("padding-right", isJsx, padding.horizontal));
|
|
537
|
+
}
|
|
538
|
+
if (padding.vertical !== 0) {
|
|
539
|
+
comp.push(formatWithJSX("padding-top", isJsx, padding.vertical));
|
|
540
|
+
comp.push(formatWithJSX("padding-bottom", isJsx, padding.vertical));
|
|
541
|
+
}
|
|
542
|
+
return comp;
|
|
543
|
+
}
|
|
544
|
+
if (padding.top !== 0) {
|
|
545
|
+
comp.push(formatWithJSX("padding-top", isJsx, padding.top));
|
|
546
|
+
}
|
|
547
|
+
if (padding.bottom !== 0) {
|
|
548
|
+
comp.push(formatWithJSX("padding-bottom", isJsx, padding.bottom));
|
|
549
|
+
}
|
|
550
|
+
if (padding.left !== 0) {
|
|
551
|
+
comp.push(formatWithJSX("padding-left", isJsx, padding.left));
|
|
552
|
+
}
|
|
553
|
+
if (padding.right !== 0) {
|
|
554
|
+
comp.push(formatWithJSX("padding-right", isJsx, padding.right));
|
|
555
|
+
}
|
|
556
|
+
return comp;
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// ../../packages/pluginMain/src/common/nodeWidthHeight.ts
|
|
560
|
+
var nodeSize = (node) => {
|
|
561
|
+
if ("layoutSizingHorizontal" in node && "layoutSizingVertical" in node) {
|
|
562
|
+
const width = node.layoutSizingHorizontal === "FILL" ? "fill" : node.layoutSizingHorizontal === "HUG" ? null : node.width;
|
|
563
|
+
const height = node.layoutSizingVertical === "FILL" ? "fill" : node.layoutSizingVertical === "HUG" ? null : node.height;
|
|
564
|
+
return { width, height };
|
|
565
|
+
}
|
|
566
|
+
return { width: node.width, height: node.height };
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlSize.ts
|
|
570
|
+
var htmlSizePartial = (node, isJsx) => {
|
|
571
|
+
if (isPreviewGlobal && node.parent === void 0) {
|
|
572
|
+
return {
|
|
573
|
+
width: formatWithJSX("width", isJsx, "100%"),
|
|
574
|
+
height: formatWithJSX("height", isJsx, "100%"),
|
|
575
|
+
constraints: []
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
const size = nodeSize(node);
|
|
579
|
+
const nodeParent = node.parent;
|
|
580
|
+
let w = "";
|
|
581
|
+
if (typeof size.width === "number") {
|
|
582
|
+
w = formatWithJSX("width", isJsx, size.width);
|
|
583
|
+
} else if (size.width === "fill") {
|
|
584
|
+
if (nodeParent && "layoutMode" in nodeParent && nodeParent.layoutMode === "HORIZONTAL") {
|
|
585
|
+
w = formatWithJSX("flex", isJsx, "1 1 0");
|
|
586
|
+
} else {
|
|
587
|
+
if (node.maxWidth) {
|
|
588
|
+
w = formatWithJSX("width", isJsx, "100%");
|
|
589
|
+
} else {
|
|
590
|
+
w = formatWithJSX("align-self", isJsx, "stretch");
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
let h = "";
|
|
595
|
+
if (typeof size.height === "number") {
|
|
596
|
+
h = formatWithJSX("height", isJsx, size.height);
|
|
597
|
+
} else if (typeof size.height === "string") {
|
|
598
|
+
if (nodeParent && "layoutMode" in nodeParent && nodeParent.layoutMode === "VERTICAL") {
|
|
599
|
+
h = formatWithJSX("flex", isJsx, "1 1 0");
|
|
600
|
+
} else {
|
|
601
|
+
if (node.maxHeight) {
|
|
602
|
+
h = formatWithJSX("height", isJsx, "100%");
|
|
603
|
+
} else {
|
|
604
|
+
h = formatWithJSX("align-self", isJsx, "stretch");
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
const constraints = [];
|
|
609
|
+
if (node.maxWidth !== void 0 && node.maxWidth !== null) {
|
|
610
|
+
constraints.push(formatWithJSX("max-width", isJsx, node.maxWidth));
|
|
611
|
+
}
|
|
612
|
+
if (node.minWidth !== void 0 && node.minWidth !== null) {
|
|
613
|
+
constraints.push(formatWithJSX("min-width", isJsx, node.minWidth));
|
|
614
|
+
}
|
|
615
|
+
if (node.maxHeight !== void 0 && node.maxHeight !== null) {
|
|
616
|
+
constraints.push(formatWithJSX("max-height", isJsx, node.maxHeight));
|
|
617
|
+
}
|
|
618
|
+
if (node.minHeight !== void 0 && node.minHeight !== null) {
|
|
619
|
+
constraints.push(formatWithJSX("min-height", isJsx, node.minHeight));
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
width: w,
|
|
623
|
+
height: h,
|
|
624
|
+
constraints
|
|
625
|
+
};
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// ../../packages/pluginMain/src/common/commonRadius.ts
|
|
629
|
+
var getCommonRadius = (node) => {
|
|
630
|
+
if ("rectangleCornerRadii" in node) {
|
|
631
|
+
const [topLeft, topRight, bottomRight, bottomLeft] = node.rectangleCornerRadii;
|
|
632
|
+
if (topLeft === topRight && topLeft === bottomRight && topLeft === bottomLeft) {
|
|
633
|
+
return { all: topLeft };
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
topLeft,
|
|
637
|
+
topRight,
|
|
638
|
+
bottomRight,
|
|
639
|
+
bottomLeft
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
if ("cornerRadius" in node && node.cornerRadius !== figma.mixed && node.cornerRadius) {
|
|
643
|
+
return { all: node.cornerRadius };
|
|
644
|
+
}
|
|
645
|
+
if ("topLeftRadius" in node) {
|
|
646
|
+
if (node.topLeftRadius === node.topRightRadius && node.topLeftRadius === node.bottomRightRadius && node.topLeftRadius === node.bottomLeftRadius) {
|
|
647
|
+
return { all: node.topLeftRadius };
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
topLeft: node.topLeftRadius,
|
|
651
|
+
topRight: node.topRightRadius,
|
|
652
|
+
bottomRight: node.bottomRightRadius,
|
|
653
|
+
bottomLeft: node.bottomLeftRadius
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
return { all: 0 };
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlBorderRadius.ts
|
|
660
|
+
var htmlBorderRadius = (node, isJsx) => {
|
|
661
|
+
const comp = [];
|
|
662
|
+
if ("children" in node && node.children.length > 0 && "clipsContent" in node && node.clipsContent) {
|
|
663
|
+
comp.push(formatWithJSX("overflow", isJsx, "hidden"));
|
|
664
|
+
}
|
|
665
|
+
if (node.type === "ELLIPSE") {
|
|
666
|
+
comp.push(formatWithJSX("border-radius", isJsx, 9999));
|
|
667
|
+
return comp;
|
|
668
|
+
}
|
|
669
|
+
const radius = getCommonRadius(node);
|
|
670
|
+
if ("all" in radius) {
|
|
671
|
+
if (radius.all === 0) {
|
|
672
|
+
return comp;
|
|
673
|
+
}
|
|
674
|
+
comp.push(formatWithJSX("border-radius", isJsx, radius.all));
|
|
675
|
+
} else {
|
|
676
|
+
const cornerValues = [radius.topLeft, radius.topRight, radius.bottomRight, radius.bottomLeft];
|
|
677
|
+
const cornerProperties = [
|
|
678
|
+
"border-top-left-radius",
|
|
679
|
+
"border-top-right-radius",
|
|
680
|
+
"border-bottom-right-radius",
|
|
681
|
+
"border-bottom-left-radius"
|
|
682
|
+
];
|
|
683
|
+
for (let i = 0; i < 4; i++) {
|
|
684
|
+
const cornerValue = cornerValues[i];
|
|
685
|
+
const cornerProperty = cornerProperties[i];
|
|
686
|
+
if (cornerValue !== void 0 && cornerProperty !== void 0 && cornerValue > 0) {
|
|
687
|
+
comp.push(formatWithJSX(cornerProperty, isJsx, cornerValue));
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return comp;
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
// ../../packages/pluginMain/src/common/commonPosition.ts
|
|
695
|
+
var getCommonPositionValue = (node, settings) => {
|
|
696
|
+
var _a;
|
|
697
|
+
const parent = node.parent;
|
|
698
|
+
if (parent && "absoluteBoundingBox" in parent && parent.absoluteBoundingBox && node.absoluteBoundingBox) {
|
|
699
|
+
if ((settings == null ? void 0 : settings.embedVectors) && "svg" in node && node.svg) {
|
|
700
|
+
return {
|
|
701
|
+
x: node.absoluteBoundingBox.x - parent.absoluteBoundingBox.x,
|
|
702
|
+
y: node.absoluteBoundingBox.y - parent.absoluteBoundingBox.y
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
return { x: node.x, y: node.y };
|
|
706
|
+
}
|
|
707
|
+
if (((_a = node.parent) == null ? void 0 : _a.type) === "GROUP") {
|
|
708
|
+
return {
|
|
709
|
+
x: node.x - node.parent.x,
|
|
710
|
+
y: node.y - node.parent.y
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
return {
|
|
714
|
+
x: node.x,
|
|
715
|
+
y: node.y
|
|
716
|
+
};
|
|
717
|
+
};
|
|
718
|
+
function calculateRectangleFromBoundingBox(boundingBox, figmaRotationDegrees) {
|
|
719
|
+
const cssRotationDegrees = -figmaRotationDegrees;
|
|
720
|
+
const theta = cssRotationDegrees * Math.PI / 180;
|
|
721
|
+
const cosTheta = Math.cos(theta);
|
|
722
|
+
const sinTheta = Math.sin(theta);
|
|
723
|
+
const absCosTheta = Math.abs(cosTheta);
|
|
724
|
+
const absSinTheta = Math.abs(sinTheta);
|
|
725
|
+
const { width: w_b, height: h_b, x: x_b, y: y_b } = boundingBox;
|
|
726
|
+
const denominator = absCosTheta * absCosTheta - absSinTheta * absSinTheta;
|
|
727
|
+
const h = (w_b * absSinTheta - h_b * absCosTheta) / -denominator;
|
|
728
|
+
const w = (w_b - h * absSinTheta) / absCosTheta;
|
|
729
|
+
const corners = [
|
|
730
|
+
{ x: 0, y: 0 },
|
|
731
|
+
{ x: w, y: 0 },
|
|
732
|
+
{ x: w, y: h },
|
|
733
|
+
{ x: 0, y: h }
|
|
734
|
+
];
|
|
735
|
+
const rotatedCorners = corners.map(({ x, y }) => ({
|
|
736
|
+
x: x * cosTheta + y * sinTheta,
|
|
737
|
+
y: -x * sinTheta + y * cosTheta
|
|
738
|
+
}));
|
|
739
|
+
const minX = Math.min(...rotatedCorners.map((c) => c.x));
|
|
740
|
+
const minY = Math.min(...rotatedCorners.map((c) => c.y));
|
|
741
|
+
const left = x_b - minX;
|
|
742
|
+
const top = y_b - minY;
|
|
743
|
+
return {
|
|
744
|
+
width: parseFloat(w.toFixed(2)),
|
|
745
|
+
height: parseFloat(h.toFixed(2)),
|
|
746
|
+
left: parseFloat(left.toFixed(2)),
|
|
747
|
+
top: parseFloat(top.toFixed(2)),
|
|
748
|
+
rotation: cssRotationDegrees
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
var commonIsAbsolutePosition = (node) => {
|
|
752
|
+
if ("layoutPositioning" in node && node.layoutPositioning === "ABSOLUTE") {
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
if (!node.parent || node.parent === void 0) {
|
|
756
|
+
return false;
|
|
757
|
+
}
|
|
758
|
+
if ("layoutMode" in node.parent && node.parent.layoutMode === "NONE" || !("layoutMode" in node.parent)) {
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
return false;
|
|
762
|
+
};
|
|
763
|
+
|
|
764
|
+
// ../../packages/pluginMain/src/common/commonStroke.ts
|
|
765
|
+
var commonStroke = (node, divideBy = 1) => {
|
|
766
|
+
if (!("strokes" in node) || !node.strokes || node.strokes.length === 0) {
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
if ("strokeTopWeight" in node) {
|
|
770
|
+
if (node.strokeTopWeight === node.strokeBottomWeight && node.strokeTopWeight === node.strokeLeftWeight && node.strokeTopWeight === node.strokeRightWeight) {
|
|
771
|
+
return { all: node.strokeTopWeight / divideBy };
|
|
772
|
+
}
|
|
773
|
+
return {
|
|
774
|
+
left: node.strokeLeftWeight / divideBy,
|
|
775
|
+
top: node.strokeTopWeight / divideBy,
|
|
776
|
+
right: node.strokeRightWeight / divideBy,
|
|
777
|
+
bottom: node.strokeBottomWeight / divideBy
|
|
778
|
+
};
|
|
779
|
+
} else if (node.strokeWeight !== figma.mixed && node.strokeWeight !== 0) {
|
|
780
|
+
return { all: node.strokeWeight / divideBy };
|
|
781
|
+
}
|
|
782
|
+
return null;
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// ../../packages/pluginMain/src/common/lowercaseFirstLetter.ts
|
|
786
|
+
function lowercaseFirstLetter(str) {
|
|
787
|
+
if (!str || str.length === 0) {
|
|
788
|
+
return str;
|
|
789
|
+
}
|
|
790
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// ../../packages/pluginMain/src/common/commonFormatAttributes.ts
|
|
794
|
+
var getClassLabel = (isJSX = false) => isJSX ? "className" : "class";
|
|
795
|
+
var joinStyles = (styles, isJSX) => styles.map((s) => s.trim()).join(isJSX ? ", " : "; ");
|
|
796
|
+
var formatStyleAttribute = (styles, isJSX) => {
|
|
797
|
+
const trimmedStyles = joinStyles(styles, isJSX);
|
|
798
|
+
if (trimmedStyles === "") return "";
|
|
799
|
+
return ` style=${isJSX ? `{{${trimmedStyles}}}` : `"${trimmedStyles}"`}`;
|
|
800
|
+
};
|
|
801
|
+
var formatDataAttribute = (label, value) => ` data-${lowercaseFirstLetter(label).replace(" ", "-")}${value === void 0 ? `` : `="${value}"`}`;
|
|
802
|
+
var formatClassAttribute = (classes, isJSX) => classes.length === 0 ? "" : ` ${getClassLabel(isJSX)}="${classes.join(" ")}"`;
|
|
803
|
+
var formatIdAttribute = (id) => id ? ` id="${id}"` : "";
|
|
804
|
+
|
|
805
|
+
// ../../packages/pluginMain/src/html/htmlDefaultBuilder.ts
|
|
806
|
+
var HtmlDefaultBuilder = class {
|
|
807
|
+
constructor(node, settings) {
|
|
808
|
+
__publicField(this, "styles");
|
|
809
|
+
__publicField(this, "data");
|
|
810
|
+
__publicField(this, "node");
|
|
811
|
+
__publicField(this, "settings");
|
|
812
|
+
__publicField(this, "addStyles", (...newStyles) => {
|
|
813
|
+
this.styles.push(...newStyles.filter((style) => style));
|
|
814
|
+
});
|
|
815
|
+
this.node = node;
|
|
816
|
+
this.settings = settings;
|
|
817
|
+
this.styles = [];
|
|
818
|
+
this.data = [];
|
|
819
|
+
}
|
|
820
|
+
get name() {
|
|
821
|
+
return this.settings.showLayerNames ? this.node.name : "";
|
|
822
|
+
}
|
|
823
|
+
get visible() {
|
|
824
|
+
return this.node.visible;
|
|
825
|
+
}
|
|
826
|
+
commonPositionStyles() {
|
|
827
|
+
this.size();
|
|
828
|
+
this.autoLayoutPadding();
|
|
829
|
+
this.position();
|
|
830
|
+
this.blend();
|
|
831
|
+
return this;
|
|
832
|
+
}
|
|
833
|
+
commonShapeStyles() {
|
|
834
|
+
if ("fills" in this.node) {
|
|
835
|
+
this.applyFillsToStyle(this.node.fills, this.node.type === "TEXT" ? "text" : "background");
|
|
836
|
+
}
|
|
837
|
+
this.shadow();
|
|
838
|
+
this.border(this.settings);
|
|
839
|
+
this.blur();
|
|
840
|
+
return this;
|
|
841
|
+
}
|
|
842
|
+
blend() {
|
|
843
|
+
const { node } = this;
|
|
844
|
+
this.addStyles(
|
|
845
|
+
htmlVisibility(node, false),
|
|
846
|
+
...htmlRotation(node, false),
|
|
847
|
+
htmlOpacity(node, false),
|
|
848
|
+
htmlBlendMode(node, false)
|
|
849
|
+
);
|
|
850
|
+
return this;
|
|
851
|
+
}
|
|
852
|
+
border(_settings) {
|
|
853
|
+
const { node } = this;
|
|
854
|
+
this.addStyles(...htmlBorderRadius(node, false));
|
|
855
|
+
const commonBorder = commonStroke(node);
|
|
856
|
+
if (!commonBorder) {
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
859
|
+
const strokes = "strokes" in node && node.strokes || void 0;
|
|
860
|
+
const color = htmlColorFromFills(strokes);
|
|
861
|
+
if (!color) {
|
|
862
|
+
return this;
|
|
863
|
+
}
|
|
864
|
+
const borderStyle = "dashPattern" in node && node.dashPattern.length > 0 ? "dotted" : "solid";
|
|
865
|
+
const strokeAlign = "strokeAlign" in node ? node.strokeAlign : "INSIDE";
|
|
866
|
+
const consolidateBorders = (border) => [formatSizeValue(border), color, borderStyle].filter((d) => d).join(" ");
|
|
867
|
+
if ("all" in commonBorder) {
|
|
868
|
+
if (commonBorder.all === 0) {
|
|
869
|
+
return this;
|
|
870
|
+
}
|
|
871
|
+
const weight = commonBorder.all;
|
|
872
|
+
if (strokeAlign === "CENTER" || strokeAlign === "OUTSIDE" || node.type === "FRAME" || node.type === "INSTANCE" || node.type === "COMPONENT") {
|
|
873
|
+
this.addStyles(formatWithJSX("outline", false, consolidateBorders(weight)));
|
|
874
|
+
if (strokeAlign === "CENTER") {
|
|
875
|
+
this.addStyles(formatWithJSX("outline-offset", false, -weight / 2));
|
|
876
|
+
} else if (strokeAlign === "INSIDE") {
|
|
877
|
+
this.addStyles(formatWithJSX("outline-offset", false, -weight));
|
|
878
|
+
}
|
|
879
|
+
} else {
|
|
880
|
+
this.addStyles(formatWithJSX("border", false, consolidateBorders(weight)));
|
|
881
|
+
}
|
|
882
|
+
} else {
|
|
883
|
+
if (commonBorder.left !== 0) {
|
|
884
|
+
this.addStyles(formatWithJSX("border-left", false, consolidateBorders(commonBorder.left)));
|
|
885
|
+
}
|
|
886
|
+
if (commonBorder.top !== 0) {
|
|
887
|
+
this.addStyles(formatWithJSX("border-top", false, consolidateBorders(commonBorder.top)));
|
|
888
|
+
}
|
|
889
|
+
if (commonBorder.right !== 0) {
|
|
890
|
+
this.addStyles(formatWithJSX("border-right", false, consolidateBorders(commonBorder.right)));
|
|
891
|
+
}
|
|
892
|
+
if (commonBorder.bottom !== 0) {
|
|
893
|
+
this.addStyles(
|
|
894
|
+
formatWithJSX("border-bottom", false, consolidateBorders(commonBorder.bottom))
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return this;
|
|
899
|
+
}
|
|
900
|
+
position() {
|
|
901
|
+
const { node } = this;
|
|
902
|
+
const isAbsolutePosition = commonIsAbsolutePosition(node);
|
|
903
|
+
if (isAbsolutePosition) {
|
|
904
|
+
const { x, y } = getCommonPositionValue(node, this.settings);
|
|
905
|
+
this.addStyles(
|
|
906
|
+
formatWithJSX("left", false, x),
|
|
907
|
+
formatWithJSX("top", false, y),
|
|
908
|
+
formatWithJSX("position", false, "absolute")
|
|
909
|
+
);
|
|
910
|
+
} else {
|
|
911
|
+
if (node.type === "GROUP" || node.isRelative) {
|
|
912
|
+
this.addStyles(formatWithJSX("position", false, "relative"));
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return this;
|
|
916
|
+
}
|
|
917
|
+
applyFillsToStyle(paintArray, property) {
|
|
918
|
+
if (property === "text") {
|
|
919
|
+
this.addStyles(formatWithJSX("text", false, htmlColorFromFills(paintArray)));
|
|
920
|
+
return this;
|
|
921
|
+
}
|
|
922
|
+
const backgroundValues = buildBackgroundValues(paintArray);
|
|
923
|
+
if (backgroundValues) {
|
|
924
|
+
this.addStyles(formatWithJSX("background", false, backgroundValues));
|
|
925
|
+
if (paintArray !== figma.mixed) {
|
|
926
|
+
const blendModes = this.buildBackgroundBlendModes(paintArray);
|
|
927
|
+
if (blendModes) {
|
|
928
|
+
this.addStyles(formatWithJSX("background-blend-mode", false, blendModes));
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return this;
|
|
933
|
+
}
|
|
934
|
+
buildBackgroundBlendModes(paintArray) {
|
|
935
|
+
if (paintArray.length === 0 || paintArray.every((d) => d.blendMode === "NORMAL" || d.blendMode === "PASS_THROUGH")) {
|
|
936
|
+
return "";
|
|
937
|
+
}
|
|
938
|
+
const blendModes = [...paintArray].reverse().map((paint) => {
|
|
939
|
+
var _a;
|
|
940
|
+
if (paint.blendMode === "PASS_THROUGH") {
|
|
941
|
+
return "normal";
|
|
942
|
+
}
|
|
943
|
+
return (_a = paint.blendMode) == null ? void 0 : _a.toLowerCase();
|
|
944
|
+
});
|
|
945
|
+
return blendModes.join(", ");
|
|
946
|
+
}
|
|
947
|
+
shadow() {
|
|
948
|
+
const { node } = this;
|
|
949
|
+
if ("effects" in node) {
|
|
950
|
+
const shadow = htmlShadow(node);
|
|
951
|
+
if (shadow) {
|
|
952
|
+
this.addStyles(formatWithJSX("box-shadow", false, htmlShadow(node)));
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return this;
|
|
956
|
+
}
|
|
957
|
+
size() {
|
|
958
|
+
const { node } = this;
|
|
959
|
+
const { width, height, constraints } = htmlSizePartial(node, false);
|
|
960
|
+
if (node.type === "TEXT") {
|
|
961
|
+
switch (node.textAutoResize) {
|
|
962
|
+
case "WIDTH_AND_HEIGHT":
|
|
963
|
+
break;
|
|
964
|
+
case "HEIGHT":
|
|
965
|
+
this.addStyles(width);
|
|
966
|
+
break;
|
|
967
|
+
case "NONE":
|
|
968
|
+
case "TRUNCATE":
|
|
969
|
+
this.addStyles(width, height);
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
} else {
|
|
973
|
+
this.addStyles(width, height);
|
|
974
|
+
}
|
|
975
|
+
if (constraints.length > 0) {
|
|
976
|
+
this.addStyles(...constraints);
|
|
977
|
+
}
|
|
978
|
+
return this;
|
|
979
|
+
}
|
|
980
|
+
autoLayoutPadding() {
|
|
981
|
+
const { node } = this;
|
|
982
|
+
if ("paddingLeft" in node) {
|
|
983
|
+
this.addStyles(...htmlPadding(node, false));
|
|
984
|
+
}
|
|
985
|
+
return this;
|
|
986
|
+
}
|
|
987
|
+
blur() {
|
|
988
|
+
const { node } = this;
|
|
989
|
+
if ("effects" in node && node.effects.length > 0) {
|
|
990
|
+
const blur = node.effects.find((e) => e.type === "LAYER_BLUR" && e.visible);
|
|
991
|
+
if (blur) {
|
|
992
|
+
this.addStyles(formatWithJSX("filter", false, `blur(${formatSizeValue(blur.radius / 2)})`));
|
|
993
|
+
}
|
|
994
|
+
const backgroundBlur = node.effects.find(
|
|
995
|
+
(e) => e.type === "BACKGROUND_BLUR" && e.visible
|
|
996
|
+
);
|
|
997
|
+
if (backgroundBlur) {
|
|
998
|
+
this.addStyles(
|
|
999
|
+
formatWithJSX(
|
|
1000
|
+
"backdrop-filter",
|
|
1001
|
+
false,
|
|
1002
|
+
`blur(${formatSizeValue(backgroundBlur.radius / 2)})`
|
|
1003
|
+
)
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
addData(label, value) {
|
|
1009
|
+
const attribute = formatDataAttribute(label, value);
|
|
1010
|
+
this.data.push(attribute);
|
|
1011
|
+
return this;
|
|
1012
|
+
}
|
|
1013
|
+
figmaIdAttribute() {
|
|
1014
|
+
return formatIdAttribute(this.node.id);
|
|
1015
|
+
}
|
|
1016
|
+
build(additionalStyle = []) {
|
|
1017
|
+
var _a;
|
|
1018
|
+
this.addStyles(...additionalStyle);
|
|
1019
|
+
const idAttribute = this.figmaIdAttribute();
|
|
1020
|
+
const classNames = [];
|
|
1021
|
+
if (this.name) {
|
|
1022
|
+
this.addData("layer", this.name.trim());
|
|
1023
|
+
const layerNameClass = stringToClassName(this.name.trim());
|
|
1024
|
+
if (layerNameClass !== "") {
|
|
1025
|
+
classNames.push(layerNameClass);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if ("componentProperties" in this.node && this.node.componentProperties) {
|
|
1029
|
+
(_a = Object.entries(this.node.componentProperties)) == null ? void 0 : _a.map((prop) => {
|
|
1030
|
+
var _a2, _b;
|
|
1031
|
+
if (prop[1].type === "VARIANT" || prop[1].type === "BOOLEAN") {
|
|
1032
|
+
const cleanName = (_b = (_a2 = prop[0].split("#")[0]) == null ? void 0 : _a2.replace(/\s+/g, "-").toLowerCase()) != null ? _b : "";
|
|
1033
|
+
return formatDataAttribute(cleanName, String(prop[1].value));
|
|
1034
|
+
}
|
|
1035
|
+
return "";
|
|
1036
|
+
}).filter(Boolean).sort().forEach((d) => this.data.push(d));
|
|
1037
|
+
}
|
|
1038
|
+
const dataAttributes = this.data.join("");
|
|
1039
|
+
const classAttribute = formatClassAttribute(classNames, false);
|
|
1040
|
+
const styleAttribute = formatStyleAttribute(this.styles, false);
|
|
1041
|
+
return `${idAttribute}${dataAttributes}${classAttribute}${styleAttribute}`;
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
// ../../packages/pluginMain/src/common/commonTextHeightSpacing.ts
|
|
1046
|
+
var commonLineHeight = (lineHeight, fontSize) => {
|
|
1047
|
+
switch (lineHeight.unit) {
|
|
1048
|
+
case "AUTO":
|
|
1049
|
+
return 0;
|
|
1050
|
+
case "PIXELS":
|
|
1051
|
+
return lineHeight.value;
|
|
1052
|
+
case "PERCENT":
|
|
1053
|
+
return fontSize * lineHeight.value / 100;
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
var commonLetterSpacing = (letterSpacing, fontSize) => {
|
|
1057
|
+
switch (letterSpacing.unit) {
|
|
1058
|
+
case "PIXELS":
|
|
1059
|
+
return letterSpacing.value;
|
|
1060
|
+
case "PERCENT":
|
|
1061
|
+
return fontSize * letterSpacing.value / 100;
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
// ../../packages/pluginMain/src/html/htmlTextBuilder.ts
|
|
1066
|
+
var HtmlTextBuilder = class extends HtmlDefaultBuilder {
|
|
1067
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor -- required to pass node/settings to HtmlDefaultBuilder
|
|
1068
|
+
constructor(node, settings) {
|
|
1069
|
+
super(node, settings);
|
|
1070
|
+
__publicField(this, "htmlElement", "p");
|
|
1071
|
+
}
|
|
1072
|
+
getTextSegments(node) {
|
|
1073
|
+
const segments = node.styledTextSegments;
|
|
1074
|
+
if (!segments) {
|
|
1075
|
+
return [];
|
|
1076
|
+
}
|
|
1077
|
+
return segments.map((segment) => {
|
|
1078
|
+
const additionalStyles = {};
|
|
1079
|
+
const layerBlurStyle = this.getLayerBlurStyle();
|
|
1080
|
+
if (layerBlurStyle) {
|
|
1081
|
+
additionalStyles.filter = layerBlurStyle;
|
|
1082
|
+
}
|
|
1083
|
+
const textShadowStyle = [this.getTextStrokeShadow(), this.getTextShadowStyle()].filter(Boolean).join(", ");
|
|
1084
|
+
if (textShadowStyle) {
|
|
1085
|
+
additionalStyles["text-shadow"] = textShadowStyle;
|
|
1086
|
+
}
|
|
1087
|
+
const styleAttributes = formatMultipleJSX(
|
|
1088
|
+
__spreadValues({
|
|
1089
|
+
color: htmlColorFromFills(segment.fills),
|
|
1090
|
+
"font-size": segment.fontSize,
|
|
1091
|
+
"font-family": segment.fontName.family,
|
|
1092
|
+
"font-style": this.getFontStyle(segment.fontName.style),
|
|
1093
|
+
"font-weight": `${segment.fontWeight}`,
|
|
1094
|
+
"text-decoration": this.textDecoration(segment.textDecoration),
|
|
1095
|
+
"text-transform": this.textTransform(segment.textCase),
|
|
1096
|
+
"line-height": this.lineHeight(segment.lineHeight, segment.fontSize),
|
|
1097
|
+
"letter-spacing": this.letterSpacing(segment.letterSpacing, segment.fontSize),
|
|
1098
|
+
"word-wrap": "break-word"
|
|
1099
|
+
}, additionalStyles),
|
|
1100
|
+
false
|
|
1101
|
+
);
|
|
1102
|
+
const charsWithLineBreak = segment.characters.split("\n").join("<br/>");
|
|
1103
|
+
return {
|
|
1104
|
+
style: styleAttributes,
|
|
1105
|
+
text: charsWithLineBreak,
|
|
1106
|
+
openTypeFeatures: segment.openTypeFeatures
|
|
1107
|
+
};
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
fontSize(node, isUI = false) {
|
|
1111
|
+
if (node.fontSize !== figma.mixed) {
|
|
1112
|
+
const value = isUI ? Math.min(node.fontSize, 24) : node.fontSize;
|
|
1113
|
+
this.addStyles(formatWithJSX("font-size", false, value));
|
|
1114
|
+
}
|
|
1115
|
+
return this;
|
|
1116
|
+
}
|
|
1117
|
+
textTrim() {
|
|
1118
|
+
if ("leadingTrim" in this.node && this.node.leadingTrim === "CAP_HEIGHT") {
|
|
1119
|
+
this.addStyles(formatWithJSX("text-box-trim", false, "trim-both"));
|
|
1120
|
+
this.addStyles(formatWithJSX("text-box-edge", false, "cap alphabetic"));
|
|
1121
|
+
}
|
|
1122
|
+
return this;
|
|
1123
|
+
}
|
|
1124
|
+
textDecoration(textDecoration) {
|
|
1125
|
+
switch (textDecoration) {
|
|
1126
|
+
case "STRIKETHROUGH":
|
|
1127
|
+
return "line-through";
|
|
1128
|
+
case "UNDERLINE":
|
|
1129
|
+
return "underline";
|
|
1130
|
+
case "NONE":
|
|
1131
|
+
return "";
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
textTransform(textCase) {
|
|
1135
|
+
switch (textCase) {
|
|
1136
|
+
case "UPPER":
|
|
1137
|
+
return "uppercase";
|
|
1138
|
+
case "LOWER":
|
|
1139
|
+
return "lowercase";
|
|
1140
|
+
case "TITLE":
|
|
1141
|
+
return "capitalize";
|
|
1142
|
+
case "ORIGINAL":
|
|
1143
|
+
case "SMALL_CAPS":
|
|
1144
|
+
case "SMALL_CAPS_FORCED":
|
|
1145
|
+
default:
|
|
1146
|
+
return "";
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
letterSpacing(letterSpacing, fontSize) {
|
|
1150
|
+
const letterSpacingProp = commonLetterSpacing(letterSpacing, fontSize);
|
|
1151
|
+
if (letterSpacingProp > 0) {
|
|
1152
|
+
return letterSpacingProp;
|
|
1153
|
+
}
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
lineHeight(lineHeight, fontSize) {
|
|
1157
|
+
const lineHeightProp = commonLineHeight(lineHeight, fontSize);
|
|
1158
|
+
if (lineHeightProp > 0) {
|
|
1159
|
+
return lineHeightProp;
|
|
1160
|
+
}
|
|
1161
|
+
return null;
|
|
1162
|
+
}
|
|
1163
|
+
getFontStyle(style) {
|
|
1164
|
+
if (/italic/.exec(style.toLowerCase())) {
|
|
1165
|
+
return "italic";
|
|
1166
|
+
}
|
|
1167
|
+
return "";
|
|
1168
|
+
}
|
|
1169
|
+
textAlignHorizontal() {
|
|
1170
|
+
const node = this.node;
|
|
1171
|
+
if (node.textAlignHorizontal && node.textAlignHorizontal !== "LEFT") {
|
|
1172
|
+
let textAlign = "";
|
|
1173
|
+
switch (node.textAlignHorizontal) {
|
|
1174
|
+
case "CENTER":
|
|
1175
|
+
textAlign = "center";
|
|
1176
|
+
break;
|
|
1177
|
+
case "RIGHT":
|
|
1178
|
+
textAlign = "right";
|
|
1179
|
+
break;
|
|
1180
|
+
case "JUSTIFIED":
|
|
1181
|
+
textAlign = "justify";
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
this.addStyles(formatWithJSX("text-align", false, textAlign));
|
|
1185
|
+
}
|
|
1186
|
+
return this;
|
|
1187
|
+
}
|
|
1188
|
+
textAlignVertical() {
|
|
1189
|
+
const node = this.node;
|
|
1190
|
+
if (node.textAlignVertical && node.textAlignVertical !== "TOP") {
|
|
1191
|
+
let alignItems = "";
|
|
1192
|
+
switch (node.textAlignVertical) {
|
|
1193
|
+
case "CENTER":
|
|
1194
|
+
alignItems = "center";
|
|
1195
|
+
break;
|
|
1196
|
+
case "BOTTOM":
|
|
1197
|
+
alignItems = "flex-end";
|
|
1198
|
+
break;
|
|
1199
|
+
}
|
|
1200
|
+
if (alignItems) {
|
|
1201
|
+
this.addStyles(formatWithJSX("justify-content", false, alignItems));
|
|
1202
|
+
this.addStyles(formatWithJSX("display", false, "flex"));
|
|
1203
|
+
this.addStyles(formatWithJSX("flex-direction", false, "column"));
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return this;
|
|
1207
|
+
}
|
|
1208
|
+
getTextStrokeShadow() {
|
|
1209
|
+
const node = this.node;
|
|
1210
|
+
const commonBorder = commonStroke(node);
|
|
1211
|
+
if (!commonBorder) {
|
|
1212
|
+
return "";
|
|
1213
|
+
}
|
|
1214
|
+
const weight = "all" in commonBorder ? commonBorder.all : Math.max(commonBorder.left, commonBorder.top, commonBorder.right, commonBorder.bottom);
|
|
1215
|
+
if (weight === 0) {
|
|
1216
|
+
return "";
|
|
1217
|
+
}
|
|
1218
|
+
const color = htmlColorFromFills("strokes" in node && node.strokes);
|
|
1219
|
+
if (!color) {
|
|
1220
|
+
return "";
|
|
1221
|
+
}
|
|
1222
|
+
const strokeAlign = "strokeAlign" in node ? node.strokeAlign : "CENTER";
|
|
1223
|
+
let radius = weight;
|
|
1224
|
+
if (strokeAlign === "CENTER" || strokeAlign === "INSIDE") {
|
|
1225
|
+
radius = weight / 2;
|
|
1226
|
+
}
|
|
1227
|
+
return this.buildTextStrokeShadow(radius, color);
|
|
1228
|
+
}
|
|
1229
|
+
buildTextStrokeShadow(radius, color) {
|
|
1230
|
+
const r = Math.max(1, Math.ceil(radius));
|
|
1231
|
+
const shadows = [];
|
|
1232
|
+
for (let x = -r; x <= r; x++) {
|
|
1233
|
+
for (let y = -r; y <= r; y++) {
|
|
1234
|
+
if (x === 0 && y === 0) {
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
if (x * x + y * y <= r * r) {
|
|
1238
|
+
shadows.push(`${formatSizeValue(x)} ${formatSizeValue(y)} 0 ${color}`);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
return shadows.join(", ");
|
|
1243
|
+
}
|
|
1244
|
+
getLayerBlurStyle() {
|
|
1245
|
+
if (this.node && this.node.effects) {
|
|
1246
|
+
const effects = this.node.effects;
|
|
1247
|
+
const blurEffect = effects.find(
|
|
1248
|
+
(effect) => effect.type === "LAYER_BLUR" && effect.visible && effect.radius > 0
|
|
1249
|
+
);
|
|
1250
|
+
if (blurEffect) {
|
|
1251
|
+
return `blur(${formatSizeValue(blurEffect.radius)})`;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return "";
|
|
1255
|
+
}
|
|
1256
|
+
getTextShadowStyle() {
|
|
1257
|
+
if (this.node && this.node.effects) {
|
|
1258
|
+
const effects = this.node.effects;
|
|
1259
|
+
const dropShadow = effects.find((effect) => effect.type === "DROP_SHADOW" && effect.visible);
|
|
1260
|
+
if (dropShadow) {
|
|
1261
|
+
const ds = dropShadow;
|
|
1262
|
+
const offsetX = Math.round(ds.offset.x);
|
|
1263
|
+
const offsetY = Math.round(ds.offset.y);
|
|
1264
|
+
const blurRadius = Math.round(ds.radius);
|
|
1265
|
+
const r = Math.round(ds.color.r * 255);
|
|
1266
|
+
const g = Math.round(ds.color.g * 255);
|
|
1267
|
+
const b = Math.round(ds.color.b * 255);
|
|
1268
|
+
const a = ds.color.a;
|
|
1269
|
+
return `${formatSizeValue(offsetX)} ${formatSizeValue(offsetY)} ${formatSizeValue(blurRadius)} rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})`;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return "";
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
|
|
1276
|
+
// ../../packages/pluginMain/src/html/builderImpl/htmlAutoLayout.ts
|
|
1277
|
+
var getFlexDirection = (node) => node.layoutMode === "HORIZONTAL" ? "" : "column";
|
|
1278
|
+
var getJustifyContent = (node) => {
|
|
1279
|
+
switch (node.primaryAxisAlignItems) {
|
|
1280
|
+
case void 0:
|
|
1281
|
+
case "MIN":
|
|
1282
|
+
return "flex-start";
|
|
1283
|
+
case "CENTER":
|
|
1284
|
+
return "center";
|
|
1285
|
+
case "MAX":
|
|
1286
|
+
return "flex-end";
|
|
1287
|
+
case "SPACE_BETWEEN":
|
|
1288
|
+
return "space-between";
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
var getAlignItems = (node) => {
|
|
1292
|
+
switch (node.counterAxisAlignItems) {
|
|
1293
|
+
case void 0:
|
|
1294
|
+
case "MIN":
|
|
1295
|
+
return "flex-start";
|
|
1296
|
+
case "CENTER":
|
|
1297
|
+
return "center";
|
|
1298
|
+
case "MAX":
|
|
1299
|
+
return "flex-end";
|
|
1300
|
+
case "BASELINE":
|
|
1301
|
+
return "baseline";
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
var getGap = (node) => node.itemSpacing > 0 && node.primaryAxisAlignItems !== "SPACE_BETWEEN" ? node.itemSpacing : "";
|
|
1305
|
+
var getFlexWrap = (node) => node.layoutWrap === "WRAP" ? "wrap" : "";
|
|
1306
|
+
var getAlignContent = (node) => {
|
|
1307
|
+
if (node.layoutWrap !== "WRAP") return "";
|
|
1308
|
+
switch (node.counterAxisAlignItems) {
|
|
1309
|
+
case void 0:
|
|
1310
|
+
case "MIN":
|
|
1311
|
+
return "flex-start";
|
|
1312
|
+
case "CENTER":
|
|
1313
|
+
return "center";
|
|
1314
|
+
case "MAX":
|
|
1315
|
+
return "flex-end";
|
|
1316
|
+
case "BASELINE":
|
|
1317
|
+
return "baseline";
|
|
1318
|
+
default:
|
|
1319
|
+
return "normal";
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
var getFlex = (node, autoLayout) => node.parent && "layoutMode" in node.parent && node.parent.layoutMode === autoLayout.layoutMode ? "flex" : "inline-flex";
|
|
1323
|
+
var htmlAutoLayoutProps = (node, _settings) => formatMultipleJSXArray(
|
|
1324
|
+
{
|
|
1325
|
+
"flex-direction": getFlexDirection(node),
|
|
1326
|
+
"justify-content": getJustifyContent(node),
|
|
1327
|
+
"align-items": getAlignItems(node),
|
|
1328
|
+
gap: getGap(node),
|
|
1329
|
+
display: getFlex(node, node),
|
|
1330
|
+
"flex-wrap": getFlexWrap(node),
|
|
1331
|
+
"align-content": getAlignContent(node)
|
|
1332
|
+
},
|
|
1333
|
+
false
|
|
1334
|
+
);
|
|
1335
|
+
|
|
1336
|
+
// ../../packages/pluginMain/src/common/curry.ts
|
|
1337
|
+
function curry(fn, arity = fn.length) {
|
|
1338
|
+
return function curried(...args) {
|
|
1339
|
+
if (args.length >= arity) {
|
|
1340
|
+
return fn(...args);
|
|
1341
|
+
}
|
|
1342
|
+
return function(...moreArgs) {
|
|
1343
|
+
return curried(...args, ...moreArgs);
|
|
1344
|
+
};
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// ../../packages/pluginMain/src/common/exportAsyncProxy.ts
|
|
1349
|
+
var isRunning = false;
|
|
1350
|
+
var exportAsyncProxy = (node, settings) => __async(null, null, function* () {
|
|
1351
|
+
if (!isRunning) {
|
|
1352
|
+
isRunning = true;
|
|
1353
|
+
yield new Promise((resolve) => setTimeout(resolve, 30));
|
|
1354
|
+
}
|
|
1355
|
+
const figmaNode = yield figma.getNodeByIdAsync(node.id);
|
|
1356
|
+
if (figmaNode.exportAsync === void 0) {
|
|
1357
|
+
throw new TypeError(
|
|
1358
|
+
"Something went wrong. This node doesn't have an exportAsync() function. Maybe check the type before calling this function."
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
let result;
|
|
1362
|
+
if (settings.format === "SVG_STRING") {
|
|
1363
|
+
result = yield figmaNode.exportAsync(settings);
|
|
1364
|
+
} else {
|
|
1365
|
+
result = yield figmaNode.exportAsync(settings);
|
|
1366
|
+
}
|
|
1367
|
+
isRunning = false;
|
|
1368
|
+
return result;
|
|
1369
|
+
});
|
|
1370
|
+
|
|
1371
|
+
// ../../packages/pluginMain/src/common/commonConversionWarnings.ts
|
|
1372
|
+
var warnings = /* @__PURE__ */ new Set();
|
|
1373
|
+
var addWarning = (warning) => {
|
|
1374
|
+
if (!warnings.has(warning)) {
|
|
1375
|
+
console.warn(warning);
|
|
1376
|
+
}
|
|
1377
|
+
warnings.add(warning);
|
|
1378
|
+
};
|
|
1379
|
+
var clearWarnings = () => {
|
|
1380
|
+
warnings.clear();
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
// ../../packages/pluginMain/src/altNodes/altNodeUtils.ts
|
|
1384
|
+
var overrideReadonlyProperty = curry(
|
|
1385
|
+
(prop, value, obj) => Object.defineProperty(obj, prop, {
|
|
1386
|
+
value,
|
|
1387
|
+
writable: true,
|
|
1388
|
+
configurable: true
|
|
1389
|
+
})
|
|
1390
|
+
);
|
|
1391
|
+
var assignParent = overrideReadonlyProperty("parent");
|
|
1392
|
+
var assignChildren = overrideReadonlyProperty("children");
|
|
1393
|
+
var assignType = overrideReadonlyProperty("type");
|
|
1394
|
+
var assignRectangleType = assignType("RECTANGLE");
|
|
1395
|
+
function isNotEmpty(value) {
|
|
1396
|
+
return value !== null && value !== void 0;
|
|
1397
|
+
}
|
|
1398
|
+
var isTypeOrGroupOfTypes = curry((matchTypes, node) => {
|
|
1399
|
+
if (matchTypes.includes(node.type)) return true;
|
|
1400
|
+
if ("children" in node) {
|
|
1401
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
1402
|
+
const childNode = node.children[i];
|
|
1403
|
+
const result = isTypeOrGroupOfTypes(matchTypes, childNode);
|
|
1404
|
+
if (!result) {
|
|
1405
|
+
return false;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return node.children.length > 0;
|
|
1409
|
+
}
|
|
1410
|
+
return false;
|
|
1411
|
+
});
|
|
1412
|
+
var renderAndAttachSVG = (node) => __async(null, null, function* () {
|
|
1413
|
+
if (node.canBeFlattened) {
|
|
1414
|
+
if (node.svg) {
|
|
1415
|
+
return node;
|
|
1416
|
+
}
|
|
1417
|
+
try {
|
|
1418
|
+
const svg = yield exportAsyncProxy(node, {
|
|
1419
|
+
format: "SVG_STRING"
|
|
1420
|
+
});
|
|
1421
|
+
if (node.colorVariableMappings && node.colorVariableMappings.size > 0) {
|
|
1422
|
+
let processedSvg = svg;
|
|
1423
|
+
const colorAttributeRegex = /(fill|stroke)="([^"]*)"/g;
|
|
1424
|
+
processedSvg = processedSvg.replace(colorAttributeRegex, (match, attribute, colorValue) => {
|
|
1425
|
+
const normalizedColor = colorValue.toLowerCase().trim();
|
|
1426
|
+
const mapping = node.colorVariableMappings.get(normalizedColor);
|
|
1427
|
+
if (mapping) {
|
|
1428
|
+
return `${attribute}="var(--${mapping.variableName}, ${colorValue})"`;
|
|
1429
|
+
}
|
|
1430
|
+
return match;
|
|
1431
|
+
});
|
|
1432
|
+
const styleRegex = /style="([^"]*)(?:(fill|stroke):\s*([^;"]*))(;|\s|")([^"]*)"/g;
|
|
1433
|
+
processedSvg = processedSvg.replace(
|
|
1434
|
+
styleRegex,
|
|
1435
|
+
(match, prefix, property, colorValue, separator, suffix) => {
|
|
1436
|
+
const normalizedColor = colorValue.toLowerCase().trim();
|
|
1437
|
+
const mapping = node.colorVariableMappings.get(normalizedColor);
|
|
1438
|
+
if (mapping) {
|
|
1439
|
+
return `style="${prefix}${property}: var(--${mapping.variableName}, ${colorValue})${separator}${suffix}"`;
|
|
1440
|
+
}
|
|
1441
|
+
return match;
|
|
1442
|
+
}
|
|
1443
|
+
);
|
|
1444
|
+
node.svg = processedSvg;
|
|
1445
|
+
} else {
|
|
1446
|
+
node.svg = svg;
|
|
1447
|
+
}
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
addWarning(`Failed rendering SVG for ${node.name}`);
|
|
1450
|
+
console.error(`Error rendering SVG for ${node.type}:${node.id}`);
|
|
1451
|
+
console.error(error);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return node;
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
// ../../packages/pluginMain/src/common/nodeVisibility.ts
|
|
1458
|
+
var getVisibleNodes = (nodes) => nodes.filter((d) => {
|
|
1459
|
+
var _a;
|
|
1460
|
+
return (_a = d.visible) != null ? _a : true;
|
|
1461
|
+
});
|
|
1462
|
+
|
|
1463
|
+
// ../../packages/pluginMain/src/common/images.ts
|
|
1464
|
+
var fillIsImage = ({ type }) => type === "IMAGE";
|
|
1465
|
+
var getImageFills = (node) => {
|
|
1466
|
+
try {
|
|
1467
|
+
return node.fills.filter(fillIsImage);
|
|
1468
|
+
} catch (e) {
|
|
1469
|
+
return [];
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
var nodeHasImageFill = (node) => getImageFills(node).length > 0;
|
|
1473
|
+
|
|
1474
|
+
// ../../packages/pluginMain/src/html/htmlMain.ts
|
|
1475
|
+
var selfClosingTags = ["img"];
|
|
1476
|
+
var isPreviewGlobal = false;
|
|
1477
|
+
function stripSvgBase64Images(svg) {
|
|
1478
|
+
return svg.replace(/data:image\/[\w+.-]+;base64,[A-Za-z0-9+/=]+/g, "");
|
|
1479
|
+
}
|
|
1480
|
+
var previousExecutionCache;
|
|
1481
|
+
var htmlMain = (sceneNode, settings, isPreview = false) => __async(null, null, function* () {
|
|
1482
|
+
yield initLayoutUnitFromStorage();
|
|
1483
|
+
isPreviewGlobal = isPreview;
|
|
1484
|
+
previousExecutionCache = [];
|
|
1485
|
+
let htmlContent = yield htmlWidgetGenerator(sceneNode, settings, isPreview);
|
|
1486
|
+
if (htmlContent.length > 0 && htmlContent.startsWith("\n")) {
|
|
1487
|
+
htmlContent = htmlContent.slice(1, htmlContent.length);
|
|
1488
|
+
}
|
|
1489
|
+
return { html: htmlContent };
|
|
1490
|
+
});
|
|
1491
|
+
var htmlWidgetGenerator = (sceneNode, settings, isPreview = false) => __async(null, null, function* () {
|
|
1492
|
+
const promiseOfConvertedCode = getVisibleNodes(sceneNode).map(convertNode(settings, isPreview));
|
|
1493
|
+
const code = (yield Promise.all(promiseOfConvertedCode)).join("");
|
|
1494
|
+
return code;
|
|
1495
|
+
});
|
|
1496
|
+
var convertNode = (settings, isPreview = false) => (node) => __async(null, null, function* () {
|
|
1497
|
+
if (settings.embedVectors && node.canBeFlattened) {
|
|
1498
|
+
const altNode = yield renderAndAttachSVG(node);
|
|
1499
|
+
if (altNode.svg) {
|
|
1500
|
+
return htmlWrapSVG(altNode, settings, isPreview);
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
switch (node.type) {
|
|
1504
|
+
case "RECTANGLE":
|
|
1505
|
+
case "ELLIPSE":
|
|
1506
|
+
return yield htmlContainer(node, "", [], settings, isPreview);
|
|
1507
|
+
case "GROUP":
|
|
1508
|
+
return yield htmlGroup(node, settings);
|
|
1509
|
+
case "FRAME":
|
|
1510
|
+
case "COMPONENT":
|
|
1511
|
+
case "INSTANCE":
|
|
1512
|
+
case "COMPONENT_SET":
|
|
1513
|
+
return yield htmlFrame(node, settings, isPreview);
|
|
1514
|
+
case "SECTION":
|
|
1515
|
+
return yield htmlSection(node, settings);
|
|
1516
|
+
case "TEXT":
|
|
1517
|
+
return htmlText(node, settings);
|
|
1518
|
+
case "LINE":
|
|
1519
|
+
return htmlLine(node, settings);
|
|
1520
|
+
case "VECTOR":
|
|
1521
|
+
if (!settings.embedVectors && !isPreviewGlobal) {
|
|
1522
|
+
addWarning("Vector is not supported");
|
|
1523
|
+
}
|
|
1524
|
+
return yield htmlContainer(
|
|
1525
|
+
__spreadProps(__spreadValues({}, node), { type: "RECTANGLE" }),
|
|
1526
|
+
"",
|
|
1527
|
+
[],
|
|
1528
|
+
settings,
|
|
1529
|
+
isPreview
|
|
1530
|
+
);
|
|
1531
|
+
default:
|
|
1532
|
+
addWarning(`${node.type} node is not supported`);
|
|
1533
|
+
return "";
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
var htmlWrapSVG = (node, settings, _isPreview = false) => {
|
|
1537
|
+
var _a;
|
|
1538
|
+
if (node.svg === "") return "";
|
|
1539
|
+
const builder = new HtmlDefaultBuilder(node, settings).addData("svg-wrapper").position();
|
|
1540
|
+
const build = builder.build();
|
|
1541
|
+
const svgContent = stripSvgBase64Images((_a = node.svg) != null ? _a : "");
|
|
1542
|
+
return `
|
|
1543
|
+
<div${build}>
|
|
1544
|
+
${indentString(svgContent)}</div>`;
|
|
1545
|
+
};
|
|
1546
|
+
var htmlGroup = (node, settings) => __async(null, null, function* () {
|
|
1547
|
+
if (node.width < 0 || node.height <= 0 || node.children.length === 0) {
|
|
1548
|
+
return "";
|
|
1549
|
+
}
|
|
1550
|
+
const builder = new HtmlDefaultBuilder(node, settings).commonPositionStyles();
|
|
1551
|
+
if (builder.styles) {
|
|
1552
|
+
const attr = builder.build();
|
|
1553
|
+
const generator = yield htmlWidgetGenerator(node.children, settings);
|
|
1554
|
+
return `
|
|
1555
|
+
<div${attr}>${indentString(generator)}
|
|
1556
|
+
</div>`;
|
|
1557
|
+
}
|
|
1558
|
+
return yield htmlWidgetGenerator(node.children, settings);
|
|
1559
|
+
});
|
|
1560
|
+
var htmlText = (node, settings) => {
|
|
1561
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1562
|
+
const layoutBuilder = new HtmlTextBuilder(node, settings).commonPositionStyles().textTrim().textAlignHorizontal().textAlignVertical();
|
|
1563
|
+
const styledHtml = layoutBuilder.getTextSegments(node);
|
|
1564
|
+
previousExecutionCache.push(...styledHtml);
|
|
1565
|
+
let content = "";
|
|
1566
|
+
if (styledHtml.length === 1) {
|
|
1567
|
+
layoutBuilder.addStyles((_b = (_a = styledHtml[0]) == null ? void 0 : _a.style) != null ? _b : "");
|
|
1568
|
+
content = (_d = (_c = styledHtml[0]) == null ? void 0 : _c.text) != null ? _d : "";
|
|
1569
|
+
const additionalTag = ((_e = styledHtml[0]) == null ? void 0 : _e.openTypeFeatures.SUBS) === true ? "sub" : ((_f = styledHtml[0]) == null ? void 0 : _f.openTypeFeatures.SUPS) === true ? "sup" : "";
|
|
1570
|
+
if (additionalTag) {
|
|
1571
|
+
content = `<${additionalTag}>${content}</${additionalTag}>`;
|
|
1572
|
+
}
|
|
1573
|
+
} else {
|
|
1574
|
+
content = styledHtml.map((style) => {
|
|
1575
|
+
const tag = style.openTypeFeatures.SUBS === true ? "sub" : style.openTypeFeatures.SUPS === true ? "sup" : "span";
|
|
1576
|
+
return `<${tag} style="${style.style}">${style.text}</${tag}>`;
|
|
1577
|
+
}).join("");
|
|
1578
|
+
}
|
|
1579
|
+
return `
|
|
1580
|
+
<div${layoutBuilder.build()}>${content}</div>`;
|
|
1581
|
+
};
|
|
1582
|
+
var htmlFrame = (node, settings, isPreview = false) => __async(null, null, function* () {
|
|
1583
|
+
const childrenStr = yield htmlWidgetGenerator(node.children, settings, isPreview);
|
|
1584
|
+
if (node.layoutMode !== "NONE") {
|
|
1585
|
+
const rowColumn = htmlAutoLayoutProps(node, settings);
|
|
1586
|
+
return yield htmlContainer(node, childrenStr, rowColumn, settings, isPreview);
|
|
1587
|
+
}
|
|
1588
|
+
return yield htmlContainer(node, childrenStr, [], settings, isPreview);
|
|
1589
|
+
});
|
|
1590
|
+
var htmlContainer = (_0, _1, ..._2) => __async(null, [_0, _1, ..._2], function* (node, children, additionalStyles = [], settings, _isPreview = false) {
|
|
1591
|
+
if (node.width <= 0 || node.height <= 0) {
|
|
1592
|
+
return children;
|
|
1593
|
+
}
|
|
1594
|
+
const builder = new HtmlDefaultBuilder(node, settings).commonPositionStyles().commonShapeStyles();
|
|
1595
|
+
if (builder.styles || additionalStyles) {
|
|
1596
|
+
let tag = "div";
|
|
1597
|
+
let src = "";
|
|
1598
|
+
if (nodeHasImageFill(node)) {
|
|
1599
|
+
const hasChildren = "children" in node && node.children.length > 0;
|
|
1600
|
+
const imgUrl = "";
|
|
1601
|
+
if (hasChildren) {
|
|
1602
|
+
builder.addStyles(formatWithJSX("background-image", false, `url(${imgUrl})`));
|
|
1603
|
+
} else {
|
|
1604
|
+
tag = "img";
|
|
1605
|
+
src = ` src="${imgUrl}"`;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
const build = builder.build(additionalStyles);
|
|
1609
|
+
if (children) {
|
|
1610
|
+
return `
|
|
1611
|
+
<${tag}${build}${src}>${indentString(children)}
|
|
1612
|
+
</${tag}>`;
|
|
1613
|
+
} else if (selfClosingTags.includes(tag)) {
|
|
1614
|
+
return `
|
|
1615
|
+
<${tag}${build}${src} />`;
|
|
1616
|
+
} else {
|
|
1617
|
+
return `
|
|
1618
|
+
<${tag}${build}${src}></${tag}>`;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return children;
|
|
1622
|
+
});
|
|
1623
|
+
var htmlSection = (node, settings) => __async(null, null, function* () {
|
|
1624
|
+
const childrenStr = yield htmlWidgetGenerator(node.children, settings);
|
|
1625
|
+
const builder = new HtmlDefaultBuilder(node, settings).size().position().applyFillsToStyle(node.fills, "background");
|
|
1626
|
+
if (childrenStr) {
|
|
1627
|
+
return `
|
|
1628
|
+
<div${builder.build()}>${indentString(childrenStr)}
|
|
1629
|
+
</div>`;
|
|
1630
|
+
} else {
|
|
1631
|
+
return `
|
|
1632
|
+
<div${builder.build()}></div>`;
|
|
1633
|
+
}
|
|
1634
|
+
});
|
|
1635
|
+
var htmlLine = (node, settings) => {
|
|
1636
|
+
const builder = new HtmlDefaultBuilder(node, settings).commonPositionStyles().commonShapeStyles();
|
|
1637
|
+
return `
|
|
1638
|
+
<div${builder.build()}></div>`;
|
|
1639
|
+
};
|
|
1640
|
+
var htmlCodeGenTextStyles = () => {
|
|
1641
|
+
const result = previousExecutionCache.map((style) => `// ${style.text}
|
|
1642
|
+
${style.style.split(";").join(";\n")}`).join("\n---\n");
|
|
1643
|
+
if (!result) {
|
|
1644
|
+
return "// No text styles in this selection";
|
|
1645
|
+
}
|
|
1646
|
+
return result;
|
|
1647
|
+
};
|
|
1648
|
+
|
|
1649
|
+
// ../../packages/pluginMain/src/common/color.ts
|
|
1650
|
+
var rgbTo6hex = (color) => {
|
|
1651
|
+
const hex = (color.r * 255 | 1 << 8).toString(16).slice(1) + (color.g * 255 | 1 << 8).toString(16).slice(1) + (color.b * 255 | 1 << 8).toString(16).slice(1);
|
|
1652
|
+
return hex;
|
|
1653
|
+
};
|
|
1654
|
+
|
|
1655
|
+
// ../../packages/pluginMain/src/common/retrieveUI/commonUI.ts
|
|
1656
|
+
var calculateContrastRatio = (color1, color2) => {
|
|
1657
|
+
const color1luminance = luminance(color1);
|
|
1658
|
+
const color2luminance = luminance(color2);
|
|
1659
|
+
const contrast = color1luminance > color2luminance ? (color2luminance + 0.05) / (color1luminance + 0.05) : (color1luminance + 0.05) / (color2luminance + 0.05);
|
|
1660
|
+
return 1 / contrast;
|
|
1661
|
+
};
|
|
1662
|
+
function luminance(color) {
|
|
1663
|
+
const a = [color.r * 255, color.g * 255, color.b * 255].map((v) => {
|
|
1664
|
+
v /= 255;
|
|
1665
|
+
return v <= 0.03928 ? v / 12.92 : __pow((v + 0.055) / 1.055, 2.4);
|
|
1666
|
+
});
|
|
1667
|
+
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// ../../packages/pluginMain/src/common/variableToColorName.ts
|
|
1671
|
+
var variableToColorName = (id) => __async(null, null, function* () {
|
|
1672
|
+
var _a;
|
|
1673
|
+
const name = (_a = yield figma.variables.getVariableByIdAsync(id)) == null ? void 0 : _a.name;
|
|
1674
|
+
if (name) {
|
|
1675
|
+
return name.replace(/\//g, "-").replace(/ /g, "-");
|
|
1676
|
+
}
|
|
1677
|
+
return id.toLowerCase().replace(/:/g, "-");
|
|
1678
|
+
});
|
|
1679
|
+
|
|
1680
|
+
// ../../packages/pluginMain/src/altNodes/iconDetection.ts
|
|
1681
|
+
var ICON_PRIMITIVE_TYPES = /* @__PURE__ */ new Set([
|
|
1682
|
+
"ELLIPSE",
|
|
1683
|
+
"RECTANGLE",
|
|
1684
|
+
"STAR",
|
|
1685
|
+
"POLYGON",
|
|
1686
|
+
"LINE"
|
|
1687
|
+
]);
|
|
1688
|
+
var ICON_COMPLEX_VECTOR_TYPES = /* @__PURE__ */ new Set(["VECTOR", "BOOLEAN_OPERATION"]);
|
|
1689
|
+
var ICON_TYPES_IGNORE_SIZE = /* @__PURE__ */ new Set([
|
|
1690
|
+
"VECTOR",
|
|
1691
|
+
"BOOLEAN_OPERATION",
|
|
1692
|
+
"POLYGON",
|
|
1693
|
+
"STAR"
|
|
1694
|
+
]);
|
|
1695
|
+
var ICON_CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1696
|
+
"FRAME",
|
|
1697
|
+
"GROUP",
|
|
1698
|
+
"COMPONENT",
|
|
1699
|
+
"INSTANCE"
|
|
1700
|
+
]);
|
|
1701
|
+
var DISALLOWED_ICON_TYPES = /* @__PURE__ */ new Set([
|
|
1702
|
+
"SLICE",
|
|
1703
|
+
"CONNECTOR",
|
|
1704
|
+
"STICKY",
|
|
1705
|
+
"SHAPE_WITH_TEXT",
|
|
1706
|
+
"CODE_BLOCK",
|
|
1707
|
+
"WIDGET",
|
|
1708
|
+
"TEXT",
|
|
1709
|
+
"COMPONENT_SET"
|
|
1710
|
+
// Component sets are containers for components, not icons themselves
|
|
1711
|
+
]);
|
|
1712
|
+
var DISALLOWED_CHILD_TYPES = /* @__PURE__ */ new Set([
|
|
1713
|
+
"FRAME",
|
|
1714
|
+
// No nested frames
|
|
1715
|
+
"COMPONENT",
|
|
1716
|
+
// No nested components
|
|
1717
|
+
"INSTANCE",
|
|
1718
|
+
// No nested instances
|
|
1719
|
+
"TEXT",
|
|
1720
|
+
// No text
|
|
1721
|
+
"SLICE",
|
|
1722
|
+
"CONNECTOR",
|
|
1723
|
+
"STICKY",
|
|
1724
|
+
"SHAPE_WITH_TEXT",
|
|
1725
|
+
"CODE_BLOCK",
|
|
1726
|
+
"WIDGET",
|
|
1727
|
+
"COMPONENT_SET"
|
|
1728
|
+
]);
|
|
1729
|
+
function isTypicalIconSize(node, maxSize = 64) {
|
|
1730
|
+
if (!("width" in node && "height" in node && node.width > 0 && node.height > 0)) {
|
|
1731
|
+
return false;
|
|
1732
|
+
}
|
|
1733
|
+
return node.width <= maxSize && node.height <= maxSize;
|
|
1734
|
+
}
|
|
1735
|
+
function hasSvgExportSettings(node) {
|
|
1736
|
+
const settingsToCheck = node.exportSettings || [];
|
|
1737
|
+
return settingsToCheck.some((setting) => setting.format === "SVG");
|
|
1738
|
+
}
|
|
1739
|
+
function checkChildrenRecursively(children) {
|
|
1740
|
+
let hasDisallowedChild = false;
|
|
1741
|
+
let hasValidContent = false;
|
|
1742
|
+
for (const child of children) {
|
|
1743
|
+
if (!child.visible) {
|
|
1744
|
+
continue;
|
|
1745
|
+
}
|
|
1746
|
+
if (DISALLOWED_CHILD_TYPES.has(child.type)) {
|
|
1747
|
+
hasDisallowedChild = true;
|
|
1748
|
+
break;
|
|
1749
|
+
}
|
|
1750
|
+
if (ICON_COMPLEX_VECTOR_TYPES.has(child.type) || ICON_PRIMITIVE_TYPES.has(child.type)) {
|
|
1751
|
+
hasValidContent = true;
|
|
1752
|
+
} else if (child.type === "GROUP" && "children" in child) {
|
|
1753
|
+
const groupResult = checkChildrenRecursively(child.children);
|
|
1754
|
+
if (groupResult.hasDisallowedChild) {
|
|
1755
|
+
hasDisallowedChild = true;
|
|
1756
|
+
break;
|
|
1757
|
+
}
|
|
1758
|
+
if (groupResult.hasValidContent) {
|
|
1759
|
+
hasValidContent = true;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
return { hasDisallowedChild, hasValidContent };
|
|
1764
|
+
}
|
|
1765
|
+
function isLikelyIcon(node, logDetails = false) {
|
|
1766
|
+
const info = [`Node: ${node.name} (${node.type}, ID: ${node.id})`];
|
|
1767
|
+
let result = false;
|
|
1768
|
+
let reason = "";
|
|
1769
|
+
if (DISALLOWED_ICON_TYPES.has(node.type)) {
|
|
1770
|
+
reason = `Disallowed Type: ${node.type}`;
|
|
1771
|
+
result = false;
|
|
1772
|
+
} else if (hasSvgExportSettings(node)) {
|
|
1773
|
+
reason = "Has SVG export settings";
|
|
1774
|
+
result = true;
|
|
1775
|
+
} else if (!("width" in node && "height" in node && node.width > 0 && node.height > 0)) {
|
|
1776
|
+
if (ICON_TYPES_IGNORE_SIZE.has(node.type)) {
|
|
1777
|
+
reason = `Direct ${node.type} type (no dimensions check needed)`;
|
|
1778
|
+
result = true;
|
|
1779
|
+
} else {
|
|
1780
|
+
reason = "No dimensions";
|
|
1781
|
+
result = false;
|
|
1782
|
+
}
|
|
1783
|
+
} else {
|
|
1784
|
+
if (ICON_TYPES_IGNORE_SIZE.has(node.type)) {
|
|
1785
|
+
reason = `Direct ${node.type} type (size ignored)`;
|
|
1786
|
+
result = true;
|
|
1787
|
+
} else if (ICON_PRIMITIVE_TYPES.has(node.type)) {
|
|
1788
|
+
if (isTypicalIconSize(node)) {
|
|
1789
|
+
reason = `Direct ${node.type} with typical size`;
|
|
1790
|
+
result = true;
|
|
1791
|
+
} else {
|
|
1792
|
+
reason = `Direct ${node.type} but too large (${Math.round(node.width)}x${Math.round(node.height)})`;
|
|
1793
|
+
result = false;
|
|
1794
|
+
}
|
|
1795
|
+
} else if (ICON_CONTAINER_TYPES.has(node.type) && "children" in node) {
|
|
1796
|
+
if (!isTypicalIconSize(node)) {
|
|
1797
|
+
reason = `Container but too large (${Math.round(node.width)}x${Math.round(node.height)})`;
|
|
1798
|
+
result = false;
|
|
1799
|
+
} else {
|
|
1800
|
+
const visibleChildren = node.children.filter((child) => child.visible);
|
|
1801
|
+
if (visibleChildren.length === 0) {
|
|
1802
|
+
const hasVisibleFill = "fills" in node && Array.isArray(node.fills) && node.fills.some(
|
|
1803
|
+
(f) => {
|
|
1804
|
+
var _a;
|
|
1805
|
+
return typeof f === "object" && f !== null && f.visible !== false && ("opacity" in f ? (_a = f.opacity) != null ? _a : 1 : 1) > 0;
|
|
1806
|
+
}
|
|
1807
|
+
);
|
|
1808
|
+
const hasVisibleStroke = "strokes" in node && Array.isArray(node.strokes) && node.strokes.some((s) => s.visible !== false);
|
|
1809
|
+
if (hasVisibleFill || hasVisibleStroke) {
|
|
1810
|
+
reason = "Empty container with visible fill/stroke and typical size";
|
|
1811
|
+
result = true;
|
|
1812
|
+
} else {
|
|
1813
|
+
reason = "Empty container with no visible style";
|
|
1814
|
+
result = false;
|
|
1815
|
+
}
|
|
1816
|
+
} else {
|
|
1817
|
+
const checkResult = checkChildrenRecursively(visibleChildren);
|
|
1818
|
+
if (checkResult.hasDisallowedChild) {
|
|
1819
|
+
reason = "Container has disallowed child type (Text, Frame, Component, Instance, etc.)";
|
|
1820
|
+
result = false;
|
|
1821
|
+
} else if (!checkResult.hasValidContent) {
|
|
1822
|
+
reason = "Container has no vector or primitive content";
|
|
1823
|
+
result = false;
|
|
1824
|
+
} else {
|
|
1825
|
+
reason = "Container with valid children and typical size";
|
|
1826
|
+
result = true;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
} else {
|
|
1831
|
+
reason = "Not a recognized icon structure (Vector, Primitive, or valid Container)";
|
|
1832
|
+
result = false;
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
info.push(`Result: ${result ? "YES" : "NO"} (${reason})`);
|
|
1836
|
+
if (logDetails) console.log(info.join(" | "));
|
|
1837
|
+
return result;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// ../../packages/pluginMain/src/altNodes/result.js
|
|
1841
|
+
var AdvancedHierarchyReorganizer = {
|
|
1842
|
+
// 统计信息
|
|
1843
|
+
stats: {
|
|
1844
|
+
totalElements: 0,
|
|
1845
|
+
migratedElements: 0,
|
|
1846
|
+
removedDuplicates: 0,
|
|
1847
|
+
preservedElements: 0,
|
|
1848
|
+
startTime: null
|
|
1849
|
+
},
|
|
1850
|
+
// 初始化统计
|
|
1851
|
+
init() {
|
|
1852
|
+
this.stats = {
|
|
1853
|
+
totalElements: 0,
|
|
1854
|
+
migratedElements: 0,
|
|
1855
|
+
removedDuplicates: 0,
|
|
1856
|
+
preservedElements: 0,
|
|
1857
|
+
startTime: Date.now()
|
|
1858
|
+
};
|
|
1859
|
+
console.log("\u{1F680} \u5F00\u59CB\u9AD8\u7EA7\u5C42\u7EA7\u91CD\u7EC4...");
|
|
1860
|
+
},
|
|
1861
|
+
// 统一的旋转处理函数 - 添加边界检查
|
|
1862
|
+
processRotation(element, relativeX, relativeY, parentElement) {
|
|
1863
|
+
if (!element.rotation || element.rotation === 0) {
|
|
1864
|
+
return { x: relativeX, y: relativeY };
|
|
1865
|
+
}
|
|
1866
|
+
console.log(`\u{1F504} \u68C0\u6D4B\u5230\u65CB\u8F6C\u5143\u7D20: "${element.name}" rotation=${element.rotation}`);
|
|
1867
|
+
let rotationDegrees;
|
|
1868
|
+
if (Math.abs(element.rotation) > 2 * Math.PI) {
|
|
1869
|
+
rotationDegrees = element.rotation;
|
|
1870
|
+
} else {
|
|
1871
|
+
rotationDegrees = element.rotation * (180 / Math.PI);
|
|
1872
|
+
}
|
|
1873
|
+
rotationDegrees = (rotationDegrees + 180) % 360 - 180;
|
|
1874
|
+
console.log(` \u{1F504} \u6807\u51C6\u5316\u65CB\u8F6C\u89D2\u5EA6: ${rotationDegrees}\xB0`);
|
|
1875
|
+
const elementBounds = this.getElementAbsoluteBounds(element);
|
|
1876
|
+
const parentBounds = this.getElementAbsoluteBounds(parentElement);
|
|
1877
|
+
if (!elementBounds || !parentBounds) {
|
|
1878
|
+
return { x: relativeX, y: relativeY };
|
|
1879
|
+
}
|
|
1880
|
+
const elementWidth = elementBounds.width;
|
|
1881
|
+
const elementHeight = elementBounds.height;
|
|
1882
|
+
const parentWidth = parentBounds.width;
|
|
1883
|
+
const parentHeight = parentBounds.height;
|
|
1884
|
+
let newX, newY;
|
|
1885
|
+
if (Math.abs(rotationDegrees) === 180) {
|
|
1886
|
+
newX = parentWidth - elementWidth - relativeX;
|
|
1887
|
+
newY = parentHeight - elementHeight - relativeY;
|
|
1888
|
+
console.log(
|
|
1889
|
+
` \u{1F504} 180\u5EA6\u65CB\u8F6C\u8C03\u6574: \u5143\u7D20\u5C3A\u5BF8(${elementWidth}x${elementHeight}), \u7236\u5143\u7D20\u5C3A\u5BF8(${parentWidth}x${parentHeight})`
|
|
1890
|
+
);
|
|
1891
|
+
console.log(` \u{1F504} \u65CB\u8F6C\u540E\u5750\u6807: (${newX}, ${newY})`);
|
|
1892
|
+
} else if (Math.abs(rotationDegrees) === 90) {
|
|
1893
|
+
if (rotationDegrees === 90) {
|
|
1894
|
+
newX = parentWidth - elementHeight - relativeY;
|
|
1895
|
+
newY = relativeX;
|
|
1896
|
+
} else {
|
|
1897
|
+
newX = relativeY;
|
|
1898
|
+
newY = parentHeight - elementWidth - relativeX;
|
|
1899
|
+
}
|
|
1900
|
+
console.log(
|
|
1901
|
+
` \u{1F504} 90\u5EA6\u65CB\u8F6C\u8C03\u6574: \u5143\u7D20\u5C3A\u5BF8(${elementWidth}x${elementHeight}), \u7236\u5143\u7D20\u5C3A\u5BF8(${parentWidth}x${parentHeight})`
|
|
1902
|
+
);
|
|
1903
|
+
console.log(` \u{1F504} \u65CB\u8F6C\u540E\u5750\u6807: (${newX}, ${newY})`);
|
|
1904
|
+
} else {
|
|
1905
|
+
const angleRad = rotationDegrees * (Math.PI / 180);
|
|
1906
|
+
const cos = Math.cos(angleRad);
|
|
1907
|
+
const sin = Math.sin(angleRad);
|
|
1908
|
+
const elementCenterX = elementBounds.x + elementWidth / 2;
|
|
1909
|
+
const elementCenterY = elementBounds.y + elementHeight / 2;
|
|
1910
|
+
const centerRelativeX = elementCenterX - parentBounds.x;
|
|
1911
|
+
const centerRelativeY = elementCenterY - parentBounds.y;
|
|
1912
|
+
newX = centerRelativeX * cos - centerRelativeY * sin;
|
|
1913
|
+
newY = centerRelativeX * sin + centerRelativeY * cos;
|
|
1914
|
+
console.log(
|
|
1915
|
+
` \u{1F504} \u590D\u6742\u65CB\u8F6C\u8C03\u6574: \u89D2\u5EA6=${rotationDegrees}\xB0, cos=${cos.toFixed(3)}, sin=${sin.toFixed(3)}`
|
|
1916
|
+
);
|
|
1917
|
+
console.log(` \u{1F504} \u65CB\u8F6C\u540E\u5750\u6807: (${newX.toFixed(1)}, ${newY.toFixed(1)})`);
|
|
1918
|
+
}
|
|
1919
|
+
const tolerance = 2;
|
|
1920
|
+
const isOutOfBounds = newX < -tolerance || newY < -tolerance || newX + elementWidth > parentWidth + tolerance || newY + elementHeight > parentHeight + tolerance;
|
|
1921
|
+
const isUnreasonablePosition = newX < -50 || newY < -50 || newX > parentWidth + 50 || newY > parentHeight + 50;
|
|
1922
|
+
const is180DegreeUnreasonable = Math.abs(rotationDegrees) === 180 && (Math.abs(newX - (parentWidth - elementWidth) / 2) > parentWidth * 0.05 || Math.abs(newY - (parentHeight - elementHeight) / 2) > parentHeight * 0.05);
|
|
1923
|
+
if (isOutOfBounds || isUnreasonablePosition || is180DegreeUnreasonable) {
|
|
1924
|
+
console.log(` \u26A0\uFE0F \u65CB\u8F6C\u540E\u8D85\u51FA\u7236\u5143\u7D20\u8FB9\u754C\u6216\u4F4D\u7F6E\u4E0D\u5408\u7406\uFF0C\u5220\u9664\u65CB\u8F6C\u5C5E\u6027`);
|
|
1925
|
+
console.log(
|
|
1926
|
+
` \u65CB\u8F6C\u540E\u8FB9\u754C: (${newX}, ${newY}) -> (${newX + elementWidth}, ${newY + elementHeight})`
|
|
1927
|
+
);
|
|
1928
|
+
console.log(` \u7236\u5143\u7D20\u8FB9\u754C: (0, 0) -> (${parentWidth}, ${parentHeight})`);
|
|
1929
|
+
console.log(
|
|
1930
|
+
` \u662F\u5426\u8D85\u51FA\u8FB9\u754C: ${isOutOfBounds}, \u662F\u5426\u4F4D\u7F6E\u4E0D\u5408\u7406: ${isUnreasonablePosition}, \u662F\u5426180\u5EA6\u4E0D\u5408\u7406: ${is180DegreeUnreasonable}`
|
|
1931
|
+
);
|
|
1932
|
+
if (element.rotation !== void 0) {
|
|
1933
|
+
console.log(` \u{1F5D1}\uFE0F \u5220\u9664rotation\u5C5E\u6027: "${element.name}" ${element.rotation} \u2192 undefined`);
|
|
1934
|
+
delete element.rotation;
|
|
1935
|
+
}
|
|
1936
|
+
if (element.cumulativeRotation !== void 0) {
|
|
1937
|
+
console.log(
|
|
1938
|
+
` \u{1F5D1}\uFE0F \u5220\u9664cumulativeRotation\u5C5E\u6027: "${element.name}" ${element.cumulativeRotation} \u2192 undefined`
|
|
1939
|
+
);
|
|
1940
|
+
delete element.cumulativeRotation;
|
|
1941
|
+
}
|
|
1942
|
+
return { x: relativeX, y: relativeY };
|
|
1943
|
+
}
|
|
1944
|
+
console.log(` \u2705 \u65CB\u8F6C\u540E\u5750\u6807\u5728\u7236\u5143\u7D20\u8FB9\u754C\u5185\uFF0C\u4F7F\u7528\u65CB\u8F6C\u540E\u7684\u5750\u6807`);
|
|
1945
|
+
return { x: newX, y: newY };
|
|
1946
|
+
},
|
|
1947
|
+
// 判断元素是否需要绝对定位 - 精确判断
|
|
1948
|
+
needsAbsolutePositioning(element, parentElement = null) {
|
|
1949
|
+
if (element.layoutPositioning === "ABSOLUTE") {
|
|
1950
|
+
return true;
|
|
1951
|
+
}
|
|
1952
|
+
if (!parentElement) {
|
|
1953
|
+
return false;
|
|
1954
|
+
}
|
|
1955
|
+
if (parentElement.layoutMode === "NONE" || !parentElement.layoutMode) {
|
|
1956
|
+
return true;
|
|
1957
|
+
}
|
|
1958
|
+
if (parentElement.layoutMode && parentElement.layoutMode !== "NONE") {
|
|
1959
|
+
if (element.rotation && element.rotation !== 0) {
|
|
1960
|
+
return true;
|
|
1961
|
+
}
|
|
1962
|
+
if (element.constraints) {
|
|
1963
|
+
const { vertical, horizontal } = element.constraints;
|
|
1964
|
+
if (vertical === "TOP" && horizontal === "LEFT") {
|
|
1965
|
+
return true;
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return false;
|
|
1970
|
+
},
|
|
1971
|
+
// 计算元素的相对坐标 - 使用统一的旋转处理
|
|
1972
|
+
calculateRelativePosition(element, parentElement = null) {
|
|
1973
|
+
if (!parentElement) {
|
|
1974
|
+
return { x: element.x || 0, y: element.y || 0 };
|
|
1975
|
+
}
|
|
1976
|
+
if (element.absoluteBoundingBox && parentElement.absoluteBoundingBox) {
|
|
1977
|
+
let relativeX = element.absoluteBoundingBox.x - parentElement.absoluteBoundingBox.x;
|
|
1978
|
+
let relativeY = element.absoluteBoundingBox.y - parentElement.absoluteBoundingBox.y;
|
|
1979
|
+
const rotationResult = this.processRotation(element, relativeX, relativeY, parentElement);
|
|
1980
|
+
relativeX = rotationResult.x;
|
|
1981
|
+
relativeY = rotationResult.y;
|
|
1982
|
+
if (element.fills && element.fills.length > 0) {
|
|
1983
|
+
const imageFill = element.fills.find((fill) => fill.type === "IMAGE" && fill.imageTransform);
|
|
1984
|
+
if (imageFill && imageFill.imageTransform) {
|
|
1985
|
+
console.log(`\u{1F5BC}\uFE0F \u68C0\u6D4B\u5230\u56FE\u50CF\u53D8\u6362: "${element.name}"`);
|
|
1986
|
+
console.log(` \u{1F5BC}\uFE0F \u56FE\u50CF\u53D8\u6362\u77E9\u9635:`, imageFill.imageTransform);
|
|
1987
|
+
const matrix = imageFill.imageTransform;
|
|
1988
|
+
if (matrix && matrix.length >= 2) {
|
|
1989
|
+
const [a, b, c] = matrix[0];
|
|
1990
|
+
const [d, e, f] = matrix[1];
|
|
1991
|
+
const transformedX = relativeX * a + relativeY * b + c;
|
|
1992
|
+
const transformedY = relativeX * d + relativeY * e + f;
|
|
1993
|
+
relativeX = transformedX;
|
|
1994
|
+
relativeY = transformedY;
|
|
1995
|
+
console.log(` \u{1F5BC}\uFE0F \u56FE\u50CF\u53D8\u6362\u540E\u5750\u6807: (${relativeX.toFixed(1)}, ${relativeY.toFixed(1)})`);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
console.log(`\u{1F522} \u5750\u6807\u8BA1\u7B97: "${element.name}" \u76F8\u5BF9\u4E8E "${parentElement.name}"`);
|
|
2000
|
+
console.log(
|
|
2001
|
+
` \u5143\u7D20\u7EDD\u5BF9\u5750\u6807: (${element.absoluteBoundingBox.x}, ${element.absoluteBoundingBox.y})`
|
|
2002
|
+
);
|
|
2003
|
+
console.log(
|
|
2004
|
+
` \u7236\u5143\u7D20\u7EDD\u5BF9\u5750\u6807: (${parentElement.absoluteBoundingBox.x}, ${parentElement.absoluteBoundingBox.y})`
|
|
2005
|
+
);
|
|
2006
|
+
console.log(` \u6700\u7EC8\u76F8\u5BF9\u5750\u6807: (${relativeX.toFixed(1)}, ${relativeY.toFixed(1)})`);
|
|
2007
|
+
return { x: relativeX, y: relativeY };
|
|
2008
|
+
}
|
|
2009
|
+
return { x: element.x || 0, y: element.y || 0 };
|
|
2010
|
+
},
|
|
2011
|
+
// 判断包含关系(使用绝对边界框)- 精确判断
|
|
2012
|
+
isElementContainedIn(element, container, tolerance = 0) {
|
|
2013
|
+
if (!element || !container) return false;
|
|
2014
|
+
const elementBounds = this.getElementAbsoluteBounds(element);
|
|
2015
|
+
const containerBounds = this.getElementAbsoluteBounds(container);
|
|
2016
|
+
const isContained = elementBounds.x >= containerBounds.x - tolerance && elementBounds.y >= containerBounds.y - tolerance && elementBounds.x + elementBounds.width <= containerBounds.x + containerBounds.width + tolerance && elementBounds.y + elementBounds.height <= containerBounds.y + containerBounds.height + tolerance;
|
|
2017
|
+
if (isContained) {
|
|
2018
|
+
if (element.id && container.id && element.id.includes(container.id)) {
|
|
2019
|
+
return false;
|
|
2020
|
+
}
|
|
2021
|
+
if (container.children && container.children.some((child) => child.id === element.id)) {
|
|
2022
|
+
return false;
|
|
2023
|
+
}
|
|
2024
|
+
}
|
|
2025
|
+
return isContained;
|
|
2026
|
+
},
|
|
2027
|
+
// 获取元素的绝对边界
|
|
2028
|
+
getElementAbsoluteBounds(element) {
|
|
2029
|
+
if (element.absoluteBoundingBox) {
|
|
2030
|
+
return {
|
|
2031
|
+
x: element.absoluteBoundingBox.x || 0,
|
|
2032
|
+
y: element.absoluteBoundingBox.y || 0,
|
|
2033
|
+
width: element.absoluteBoundingBox.width || element.width || 0,
|
|
2034
|
+
height: element.absoluteBoundingBox.height || element.height || 0
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
return {
|
|
2038
|
+
x: element.x || 0,
|
|
2039
|
+
y: element.y || 0,
|
|
2040
|
+
width: element.width || 0,
|
|
2041
|
+
height: element.height || 0
|
|
2042
|
+
};
|
|
2043
|
+
},
|
|
2044
|
+
// 检查元素是否应该被迁移
|
|
2045
|
+
shouldMigrateElement(element, currentParent, potentialParent) {
|
|
2046
|
+
if (!element || !potentialParent) return false;
|
|
2047
|
+
const isContained = this.isElementContainedIn(element, potentialParent);
|
|
2048
|
+
if (!isContained) return false;
|
|
2049
|
+
const needsAbsolute = this.needsAbsolutePositioning(element, potentialParent);
|
|
2050
|
+
if (!needsAbsolute) return false;
|
|
2051
|
+
if (currentParent && currentParent.id === potentialParent.id) return false;
|
|
2052
|
+
if (!this.isCompatibleParent(element, potentialParent)) return false;
|
|
2053
|
+
return true;
|
|
2054
|
+
},
|
|
2055
|
+
// 检查父元素兼容性 - 考虑type属性
|
|
2056
|
+
isCompatibleParent(element, parent) {
|
|
2057
|
+
if (!parent || !element) return false;
|
|
2058
|
+
if (parent.id === element.id) return false;
|
|
2059
|
+
const parentType = parent.type;
|
|
2060
|
+
const elementType = element.type;
|
|
2061
|
+
const containerTypes = ["FRAME", "COMPONENT", "INSTANCE", "COMPONENT_SET", "GROUP", "SECTION"];
|
|
2062
|
+
if (!containerTypes.includes(parentType)) {
|
|
2063
|
+
console.log(`\u26A0\uFE0F \u7236\u5143\u7D20\u7C7B\u578B\u4E0D\u517C\u5BB9: "${parent.name}" (${parentType}) \u4E0D\u80FD\u4F5C\u4E3A\u5BB9\u5668`);
|
|
2064
|
+
return false;
|
|
2065
|
+
}
|
|
2066
|
+
return true;
|
|
2067
|
+
},
|
|
2068
|
+
// 迁移元素到新的父元素 - 精确迁移
|
|
2069
|
+
migrateElement(element, newParent, originalParent = null) {
|
|
2070
|
+
if (!element || !newParent) return;
|
|
2071
|
+
console.log(`\u{1F504} \u8FC1\u79FB\u5143\u7D20: "${element.name}" \u2192 "${newParent.name}"`);
|
|
2072
|
+
if (originalParent && originalParent.children) {
|
|
2073
|
+
const index = originalParent.children.indexOf(element);
|
|
2074
|
+
if (index > -1) {
|
|
2075
|
+
originalParent.children.splice(index, 1);
|
|
2076
|
+
console.log(` \u{1F4E4} \u4ECE "${originalParent.name}" \u79FB\u9664`);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
if (!newParent.children) {
|
|
2080
|
+
newParent.children = [];
|
|
2081
|
+
}
|
|
2082
|
+
newParent.children.push(element);
|
|
2083
|
+
element.parent = newParent;
|
|
2084
|
+
console.log(` \u{1F4E5} \u6DFB\u52A0\u5230 "${newParent.name}"`);
|
|
2085
|
+
this.updateRotationProperties(element, newParent, originalParent);
|
|
2086
|
+
const newPosition = this.calculateRelativePosition(element, newParent);
|
|
2087
|
+
const oldX = element.x || 0;
|
|
2088
|
+
const oldY = element.y || 0;
|
|
2089
|
+
element.x = newPosition.x;
|
|
2090
|
+
element.y = newPosition.y;
|
|
2091
|
+
console.log(` \u{1F4CD} \u5750\u6807\u66F4\u65B0: (${oldX}, ${oldY}) \u2192 (${newPosition.x}, ${newPosition.y})`);
|
|
2092
|
+
this.updateElementPositioning(element, newParent);
|
|
2093
|
+
this.ensureCorrectType(element, newParent);
|
|
2094
|
+
this.stats.migratedElements++;
|
|
2095
|
+
},
|
|
2096
|
+
// 更新旋转属性 - 处理rotation和cumulativeRotation
|
|
2097
|
+
updateRotationProperties(element, newParent, originalParent) {
|
|
2098
|
+
if (!element || !newParent) return;
|
|
2099
|
+
console.log(`\u{1F504} \u65CB\u8F6C\u5C5E\u6027\u66F4\u65B0: "${element.name}"`);
|
|
2100
|
+
console.log(` \u539F\u7236\u5143\u7D20: "${(originalParent == null ? void 0 : originalParent.name) || "\u65E0"}"`);
|
|
2101
|
+
console.log(` \u65B0\u7236\u5143\u7D20: "${newParent.name}"`);
|
|
2102
|
+
let originalCumulativeRotation = 0;
|
|
2103
|
+
if (originalParent) {
|
|
2104
|
+
let currentParent2 = originalParent;
|
|
2105
|
+
while (currentParent2) {
|
|
2106
|
+
if (currentParent2.rotation !== void 0 && currentParent2.rotation !== 0) {
|
|
2107
|
+
originalCumulativeRotation += currentParent2.rotation;
|
|
2108
|
+
}
|
|
2109
|
+
currentParent2 = currentParent2.parent;
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
let newCumulativeRotation = 0;
|
|
2113
|
+
let currentParent = newParent;
|
|
2114
|
+
while (currentParent) {
|
|
2115
|
+
if (currentParent.rotation !== void 0 && currentParent.rotation !== 0) {
|
|
2116
|
+
newCumulativeRotation += currentParent.rotation;
|
|
2117
|
+
}
|
|
2118
|
+
currentParent = currentParent.parent;
|
|
2119
|
+
}
|
|
2120
|
+
const originalRotation = element.rotation || 0;
|
|
2121
|
+
const originalCumulativeRotationValue = element.cumulativeRotation || 0;
|
|
2122
|
+
const newParentHasRotation = newParent.rotation !== void 0 && newParent.rotation !== 0;
|
|
2123
|
+
const newParentChainHasRotation = newCumulativeRotation !== 0;
|
|
2124
|
+
if (!newParentHasRotation && !newParentChainHasRotation) {
|
|
2125
|
+
if (element.rotation !== void 0) {
|
|
2126
|
+
console.log(` \u{1F5D1}\uFE0F \u5220\u9664rotation\u5C5E\u6027: "${element.name}" ${element.rotation} \u2192 undefined`);
|
|
2127
|
+
delete element.rotation;
|
|
2128
|
+
}
|
|
2129
|
+
if (element.cumulativeRotation !== void 0) {
|
|
2130
|
+
console.log(
|
|
2131
|
+
` \u{1F5D1}\uFE0F \u5220\u9664cumulativeRotation\u5C5E\u6027: "${element.name}" ${element.cumulativeRotation} \u2192 undefined`
|
|
2132
|
+
);
|
|
2133
|
+
delete element.cumulativeRotation;
|
|
2134
|
+
}
|
|
2135
|
+
} else {
|
|
2136
|
+
const totalOriginalRotation = originalRotation + originalCumulativeRotationValue;
|
|
2137
|
+
const newRotation = totalOriginalRotation - newCumulativeRotation;
|
|
2138
|
+
if (newRotation !== 0) {
|
|
2139
|
+
element.rotation = newRotation;
|
|
2140
|
+
console.log(` \u{1F504} \u66F4\u65B0rotation: "${element.name}" ${originalRotation} \u2192 ${newRotation}`);
|
|
2141
|
+
} else {
|
|
2142
|
+
if (element.rotation !== void 0) {
|
|
2143
|
+
console.log(` \u{1F5D1}\uFE0F \u5220\u9664rotation\u5C5E\u6027: "${element.name}" ${element.rotation} \u2192 undefined`);
|
|
2144
|
+
delete element.rotation;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
element.cumulativeRotation = newCumulativeRotation;
|
|
2148
|
+
console.log(
|
|
2149
|
+
` \u{1F504} \u66F4\u65B0cumulativeRotation: "${element.name}" ${originalCumulativeRotationValue} \u2192 ${newCumulativeRotation}`
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
const totalRotation = (element.rotation || 0) + (element.cumulativeRotation || 0);
|
|
2153
|
+
if (Math.abs(totalRotation) > 360) {
|
|
2154
|
+
console.warn(`\u26A0\uFE0F \u65CB\u8F6C\u89D2\u5EA6\u5F02\u5E38: "${element.name}" totalRotation=${totalRotation}`);
|
|
2155
|
+
}
|
|
2156
|
+
console.log(` \u2705 \u65CB\u8F6C\u5C5E\u6027\u66F4\u65B0\u5B8C\u6210: "${element.name}"`);
|
|
2157
|
+
console.log(` rotation: ${element.rotation !== void 0 ? element.rotation : "undefined"}`);
|
|
2158
|
+
console.log(
|
|
2159
|
+
` cumulativeRotation: ${element.cumulativeRotation !== void 0 ? element.cumulativeRotation : "undefined"}`
|
|
2160
|
+
);
|
|
2161
|
+
console.log(` \u603B\u65CB\u8F6C\u89D2\u5EA6: ${totalRotation}\xB0`);
|
|
2162
|
+
},
|
|
2163
|
+
// 更新元素的定位属性 - 精确处理所有关键属性
|
|
2164
|
+
updateElementPositioning(element, parentElement) {
|
|
2165
|
+
if (!element || !parentElement) return;
|
|
2166
|
+
if (!element.id) {
|
|
2167
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11ID: "${element.name}"`);
|
|
2168
|
+
}
|
|
2169
|
+
if (!element.absoluteBoundingBox) {
|
|
2170
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11absoluteBoundingBox: "${element.name}"`);
|
|
2171
|
+
} else {
|
|
2172
|
+
const { x, y, width, height } = element.absoluteBoundingBox;
|
|
2173
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0) {
|
|
2174
|
+
console.warn(`\u26A0\uFE0F absoluteBoundingBox\u4E0D\u5B8C\u6574: "${element.name}"`, element.absoluteBoundingBox);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
if (!element.absoluteRenderBounds) {
|
|
2178
|
+
if (element.absoluteBoundingBox) {
|
|
2179
|
+
element.absoluteRenderBounds = __spreadValues({}, element.absoluteBoundingBox);
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
if (parentElement.layoutMode && parentElement.layoutMode !== "NONE") {
|
|
2183
|
+
delete element.layoutPositioning;
|
|
2184
|
+
element.layoutGrow = element.layoutGrow || 0;
|
|
2185
|
+
element.layoutSizingHorizontal = element.layoutSizingHorizontal || "FIXED";
|
|
2186
|
+
element.layoutSizingVertical = element.layoutSizingVertical || "FIXED";
|
|
2187
|
+
} else {
|
|
2188
|
+
element.layoutPositioning = "ABSOLUTE";
|
|
2189
|
+
delete element.layoutGrow;
|
|
2190
|
+
delete element.layoutSizingHorizontal;
|
|
2191
|
+
delete element.layoutSizingVertical;
|
|
2192
|
+
}
|
|
2193
|
+
if (element.x === void 0 || element.y === void 0) {
|
|
2194
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11\u5750\u6807: "${element.name}" x=${element.x}, y=${element.y}`);
|
|
2195
|
+
}
|
|
2196
|
+
if (!element.constraints) {
|
|
2197
|
+
element.constraints = {
|
|
2198
|
+
vertical: "TOP",
|
|
2199
|
+
horizontal: "LEFT"
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
console.log(
|
|
2203
|
+
` \u{1F527} \u66F4\u65B0\u5B9A\u4F4D\u5C5E\u6027: "${element.name}" layoutPositioning=${element.layoutPositioning || "undefined"}, x=${element.x}, y=${element.y}`
|
|
2204
|
+
);
|
|
2205
|
+
},
|
|
2206
|
+
// 确保type属性正确 - 根据HTML生成逻辑
|
|
2207
|
+
ensureCorrectType(element, parentElement) {
|
|
2208
|
+
if (!element) return;
|
|
2209
|
+
const hasChildren = element.children && element.children.length > 0;
|
|
2210
|
+
const originalType = element.type;
|
|
2211
|
+
let newType = originalType;
|
|
2212
|
+
if (hasChildren) {
|
|
2213
|
+
switch (originalType) {
|
|
2214
|
+
case "RECTANGLE":
|
|
2215
|
+
case "ELLIPSE":
|
|
2216
|
+
case "LINE":
|
|
2217
|
+
case "VECTOR":
|
|
2218
|
+
case "TEXT":
|
|
2219
|
+
newType = "FRAME";
|
|
2220
|
+
break;
|
|
2221
|
+
case "FRAME":
|
|
2222
|
+
case "COMPONENT":
|
|
2223
|
+
case "INSTANCE":
|
|
2224
|
+
case "COMPONENT_SET":
|
|
2225
|
+
case "GROUP":
|
|
2226
|
+
case "SECTION":
|
|
2227
|
+
break;
|
|
2228
|
+
default:
|
|
2229
|
+
newType = "FRAME";
|
|
2230
|
+
break;
|
|
2231
|
+
}
|
|
2232
|
+
} else {
|
|
2233
|
+
switch (originalType) {
|
|
2234
|
+
case "FRAME":
|
|
2235
|
+
case "COMPONENT":
|
|
2236
|
+
case "INSTANCE":
|
|
2237
|
+
case "COMPONENT_SET":
|
|
2238
|
+
case "GROUP":
|
|
2239
|
+
case "SECTION":
|
|
2240
|
+
newType = "RECTANGLE";
|
|
2241
|
+
break;
|
|
2242
|
+
case "RECTANGLE":
|
|
2243
|
+
case "ELLIPSE":
|
|
2244
|
+
case "TEXT":
|
|
2245
|
+
case "LINE":
|
|
2246
|
+
case "VECTOR":
|
|
2247
|
+
break;
|
|
2248
|
+
default:
|
|
2249
|
+
newType = "RECTANGLE";
|
|
2250
|
+
break;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
if (newType !== originalType) {
|
|
2254
|
+
console.log(
|
|
2255
|
+
` \u{1F504} \u8C03\u6574type: "${element.name}" ${originalType} \u2192 ${newType} (${hasChildren ? "\u6709\u5B50\u5143\u7D20" : "\u65E0\u5B50\u5143\u7D20"})`
|
|
2256
|
+
);
|
|
2257
|
+
element.type = newType;
|
|
2258
|
+
}
|
|
2259
|
+
},
|
|
2260
|
+
// 移除重复元素
|
|
2261
|
+
removeDuplicateElements(elements) {
|
|
2262
|
+
const uniqueElements = [];
|
|
2263
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2264
|
+
for (const element of elements) {
|
|
2265
|
+
if (!seen.has(element.id)) {
|
|
2266
|
+
seen.add(element.id);
|
|
2267
|
+
uniqueElements.push(element);
|
|
2268
|
+
} else {
|
|
2269
|
+
this.stats.removedDuplicates++;
|
|
2270
|
+
console.log(`\u{1F5D1}\uFE0F \u79FB\u9664\u91CD\u590D\u5143\u7D20: "${element.name}"`);
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
return uniqueElements;
|
|
2274
|
+
},
|
|
2275
|
+
// 处理单个层级的元素
|
|
2276
|
+
processLevel(elements, parentChain = []) {
|
|
2277
|
+
if (!elements || elements.length === 0) return elements;
|
|
2278
|
+
this.stats.totalElements += elements.length;
|
|
2279
|
+
const uniqueElements = this.removeDuplicateElements(elements);
|
|
2280
|
+
for (let i = 0; i < uniqueElements.length; i++) {
|
|
2281
|
+
const element = uniqueElements[i];
|
|
2282
|
+
for (let j = 0; j < uniqueElements.length; j++) {
|
|
2283
|
+
if (i === j) continue;
|
|
2284
|
+
const potentialParent = uniqueElements[j];
|
|
2285
|
+
if (this.shouldMigrateElement(element, element.parent, potentialParent)) {
|
|
2286
|
+
this.migrateElement(element, potentialParent, element.parent);
|
|
2287
|
+
uniqueElements.splice(i, 1);
|
|
2288
|
+
i--;
|
|
2289
|
+
break;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
return uniqueElements;
|
|
2294
|
+
},
|
|
2295
|
+
// 递归处理层级结构
|
|
2296
|
+
processHierarchy(element, parentChain = []) {
|
|
2297
|
+
if (!element) return;
|
|
2298
|
+
if (element.children && element.children.length > 0) {
|
|
2299
|
+
element.children = this.processLevel(element.children, [...parentChain, element]);
|
|
2300
|
+
for (const child of element.children) {
|
|
2301
|
+
this.processHierarchy(child, [...parentChain, element]);
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
},
|
|
2305
|
+
// 重新计算所有元素的坐标
|
|
2306
|
+
recalculateAllCoordinates(rootElement) {
|
|
2307
|
+
console.log(`\u{1F3AF} \u91CD\u65B0\u8BA1\u7B97\u5750\u6807: "${rootElement.name}"`);
|
|
2308
|
+
this.recalculateElementCoordinates(rootElement, null);
|
|
2309
|
+
console.log("\u2705 \u5750\u6807\u91CD\u8BA1\u7B97\u5B8C\u6210!");
|
|
2310
|
+
},
|
|
2311
|
+
// 递归计算元素及其子元素的坐标
|
|
2312
|
+
recalculateElementCoordinates(element, parentElement = null, visited = /* @__PURE__ */ new Set()) {
|
|
2313
|
+
if (!element) return;
|
|
2314
|
+
if (visited.has(element.id)) {
|
|
2315
|
+
console.warn(`\u26A0\uFE0F \u68C0\u6D4B\u5230\u5FAA\u73AF\u5F15\u7528\uFF0C\u8DF3\u8FC7\u5143\u7D20: "${element.name}" (${element.id})`);
|
|
2316
|
+
return;
|
|
2317
|
+
}
|
|
2318
|
+
visited.add(element.id);
|
|
2319
|
+
if (parentElement) {
|
|
2320
|
+
const needsAbsolute = this.needsAbsolutePositioning(element, parentElement);
|
|
2321
|
+
if (needsAbsolute) {
|
|
2322
|
+
const newPosition = this.calculateRelativePosition(element, parentElement);
|
|
2323
|
+
if (element.x !== newPosition.x || element.y !== newPosition.y) {
|
|
2324
|
+
const oldX = element.x || 0;
|
|
2325
|
+
const oldY = element.y || 0;
|
|
2326
|
+
element.x = newPosition.x;
|
|
2327
|
+
element.y = newPosition.y;
|
|
2328
|
+
if (Math.abs(oldX - newPosition.x) > 1 || Math.abs(oldY - newPosition.y) > 1) {
|
|
2329
|
+
console.log(
|
|
2330
|
+
`\u{1F4CD} \u5750\u6807\u66F4\u65B0: "${element.name}" (${oldX}, ${oldY}) \u2192 (${newPosition.x}, ${newPosition.y})`
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
if (!element.layoutPositioning) {
|
|
2335
|
+
element.layoutPositioning = "ABSOLUTE";
|
|
2336
|
+
}
|
|
2337
|
+
} else {
|
|
2338
|
+
if (element.layoutPositioning === "ABSOLUTE") {
|
|
2339
|
+
delete element.layoutPositioning;
|
|
2340
|
+
console.log(`\u{1F504} \u79FB\u9664\u7EDD\u5BF9\u5B9A\u4F4D: "${element.name}"`);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
if (element.children && element.children.length > 0) {
|
|
2345
|
+
for (const child of element.children) {
|
|
2346
|
+
this.recalculateElementCoordinates(child, element, visited);
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
visited.delete(element.id);
|
|
2350
|
+
},
|
|
2351
|
+
// 同步父子元素属性 - 精确同步
|
|
2352
|
+
syncParentChildProperties(rootElement) {
|
|
2353
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2354
|
+
const syncRecursive = (element, parentElement = null) => {
|
|
2355
|
+
if (!element || visited.has(element.id)) return;
|
|
2356
|
+
visited.add(element.id);
|
|
2357
|
+
if (parentElement) {
|
|
2358
|
+
element.parent = parentElement;
|
|
2359
|
+
if (!parentElement.children) {
|
|
2360
|
+
parentElement.children = [];
|
|
2361
|
+
}
|
|
2362
|
+
if (!parentElement.children.find((child) => child.id === element.id)) {
|
|
2363
|
+
parentElement.children.push(element);
|
|
2364
|
+
}
|
|
2365
|
+
} else {
|
|
2366
|
+
element.parent = null;
|
|
2367
|
+
}
|
|
2368
|
+
this.syncLayoutProperties(element, parentElement);
|
|
2369
|
+
this.syncConstraints(element, parentElement);
|
|
2370
|
+
this.syncPositioningProperties(element, parentElement);
|
|
2371
|
+
this.syncRotationProperties(element, parentElement);
|
|
2372
|
+
if (element.children && element.children.length > 0) {
|
|
2373
|
+
for (const child of element.children) {
|
|
2374
|
+
syncRecursive(child, element);
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
};
|
|
2378
|
+
syncRecursive(rootElement);
|
|
2379
|
+
console.log("\u2705 \u7236\u5B50\u5C5E\u6027\u540C\u6B65\u5B8C\u6210");
|
|
2380
|
+
},
|
|
2381
|
+
// 同步旋转属性 - 确保rotation和cumulativeRotation正确
|
|
2382
|
+
syncRotationProperties(element, parentElement) {
|
|
2383
|
+
if (!element) return;
|
|
2384
|
+
if (element.rotation === void 0) {
|
|
2385
|
+
element.rotation = 0;
|
|
2386
|
+
}
|
|
2387
|
+
if (element.cumulativeRotation === void 0) {
|
|
2388
|
+
element.cumulativeRotation = 0;
|
|
2389
|
+
}
|
|
2390
|
+
if (isNaN(element.rotation) || isNaN(element.cumulativeRotation)) {
|
|
2391
|
+
element.rotation = 0;
|
|
2392
|
+
element.cumulativeRotation = 0;
|
|
2393
|
+
console.log(`\u{1F527} \u4FEE\u590D\u65E0\u6548\u65CB\u8F6C\u5C5E\u6027: "${element.name}"`);
|
|
2394
|
+
}
|
|
2395
|
+
const totalRotation = (element.rotation || 0) + (element.cumulativeRotation || 0);
|
|
2396
|
+
if (Math.abs(totalRotation) > 360) {
|
|
2397
|
+
console.warn(`\u26A0\uFE0F \u65CB\u8F6C\u89D2\u5EA6\u5F02\u5E38: "${element.name}" totalRotation=${totalRotation}`);
|
|
2398
|
+
}
|
|
2399
|
+
if (parentElement) {
|
|
2400
|
+
let expectedCumulativeRotation = 0;
|
|
2401
|
+
let currentParent = parentElement;
|
|
2402
|
+
while (currentParent) {
|
|
2403
|
+
if (currentParent.rotation !== void 0 && currentParent.rotation !== 0) {
|
|
2404
|
+
expectedCumulativeRotation += currentParent.rotation;
|
|
2405
|
+
}
|
|
2406
|
+
currentParent = currentParent.parent;
|
|
2407
|
+
}
|
|
2408
|
+
if (Math.abs(element.cumulativeRotation - expectedCumulativeRotation) > 0.1) {
|
|
2409
|
+
const oldCumulativeRotation = element.cumulativeRotation;
|
|
2410
|
+
element.cumulativeRotation = expectedCumulativeRotation;
|
|
2411
|
+
console.log(
|
|
2412
|
+
`\u{1F527} \u4FEE\u590DcumulativeRotation: "${element.name}" ${oldCumulativeRotation} \u2192 ${expectedCumulativeRotation}`
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
},
|
|
2417
|
+
// 同步定位属性 - 精确处理所有关键属性
|
|
2418
|
+
syncPositioningProperties(element, parentElement) {
|
|
2419
|
+
if (!element) return;
|
|
2420
|
+
if (!element.id) {
|
|
2421
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11ID: "${element.name}"`);
|
|
2422
|
+
}
|
|
2423
|
+
if (!element.absoluteBoundingBox) {
|
|
2424
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11absoluteBoundingBox: "${element.name}"`);
|
|
2425
|
+
} else {
|
|
2426
|
+
const { x, y, width, height } = element.absoluteBoundingBox;
|
|
2427
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0) {
|
|
2428
|
+
console.warn(`\u26A0\uFE0F absoluteBoundingBox\u4E0D\u5B8C\u6574: "${element.name}"`, element.absoluteBoundingBox);
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
if (!element.absoluteRenderBounds) {
|
|
2432
|
+
if (element.absoluteBoundingBox) {
|
|
2433
|
+
element.absoluteRenderBounds = __spreadValues({}, element.absoluteBoundingBox);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
if (element.x === void 0 || element.y === void 0) {
|
|
2437
|
+
if (element.absoluteBoundingBox) {
|
|
2438
|
+
if (parentElement && parentElement.absoluteBoundingBox) {
|
|
2439
|
+
element.x = element.absoluteBoundingBox.x - parentElement.absoluteBoundingBox.x;
|
|
2440
|
+
element.y = element.absoluteBoundingBox.y - parentElement.absoluteBoundingBox.y;
|
|
2441
|
+
} else {
|
|
2442
|
+
element.x = element.absoluteBoundingBox.x;
|
|
2443
|
+
element.y = element.absoluteBoundingBox.y;
|
|
2444
|
+
}
|
|
2445
|
+
} else {
|
|
2446
|
+
element.x = 0;
|
|
2447
|
+
element.y = 0;
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
if (element.width === void 0 || element.height === void 0) {
|
|
2451
|
+
if (element.absoluteBoundingBox) {
|
|
2452
|
+
element.width = element.absoluteBoundingBox.width;
|
|
2453
|
+
element.height = element.absoluteBoundingBox.height;
|
|
2454
|
+
} else {
|
|
2455
|
+
element.width = 0;
|
|
2456
|
+
element.height = 0;
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
if (parentElement) {
|
|
2460
|
+
const needsAbsolute = this.needsAbsolutePositioning(element, parentElement);
|
|
2461
|
+
if (needsAbsolute) {
|
|
2462
|
+
element.layoutPositioning = "ABSOLUTE";
|
|
2463
|
+
if (!element.constraints) {
|
|
2464
|
+
element.constraints = {
|
|
2465
|
+
vertical: "TOP",
|
|
2466
|
+
horizontal: "LEFT"
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
} else {
|
|
2470
|
+
delete element.layoutPositioning;
|
|
2471
|
+
if (!element.constraints) {
|
|
2472
|
+
element.constraints = {
|
|
2473
|
+
vertical: "TOP",
|
|
2474
|
+
horizontal: "LEFT"
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
this.validateElementProperties(element);
|
|
2480
|
+
},
|
|
2481
|
+
// 验证元素属性的完整性
|
|
2482
|
+
validateElementProperties(element) {
|
|
2483
|
+
const requiredProps = ["id", "name", "type"];
|
|
2484
|
+
const missingProps = requiredProps.filter((prop) => !element[prop]);
|
|
2485
|
+
if (missingProps.length > 0) {
|
|
2486
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11\u5FC5\u8981\u5C5E\u6027: "${element.name}" \u7F3A\u5C11: ${missingProps.join(", ")}`);
|
|
2487
|
+
}
|
|
2488
|
+
if (element.type) {
|
|
2489
|
+
const validTypes = [
|
|
2490
|
+
"RECTANGLE",
|
|
2491
|
+
"ELLIPSE",
|
|
2492
|
+
"TEXT",
|
|
2493
|
+
"FRAME",
|
|
2494
|
+
"GROUP",
|
|
2495
|
+
"INSTANCE",
|
|
2496
|
+
"COMPONENT",
|
|
2497
|
+
"COMPONENT_SET",
|
|
2498
|
+
"SECTION",
|
|
2499
|
+
"LINE",
|
|
2500
|
+
"VECTOR"
|
|
2501
|
+
];
|
|
2502
|
+
if (!validTypes.includes(element.type)) {
|
|
2503
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20type\u5C5E\u6027\u65E0\u6548: "${element.name}" type=${element.type}`);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
if (typeof element.x !== "number" || typeof element.y !== "number") {
|
|
2507
|
+
console.warn(
|
|
2508
|
+
`\u26A0\uFE0F \u5143\u7D20\u5750\u6807\u7C7B\u578B\u9519\u8BEF: "${element.name}" x=${typeof element.x}, y=${typeof element.y}`
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2511
|
+
if (typeof element.width !== "number" || typeof element.height !== "number") {
|
|
2512
|
+
console.warn(
|
|
2513
|
+
`\u26A0\uFE0F \u5143\u7D20\u5C3A\u5BF8\u7C7B\u578B\u9519\u8BEF: "${element.name}" width=${typeof element.width}, height=${typeof element.height}`
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
if (element.absoluteBoundingBox) {
|
|
2517
|
+
const { x, y, width, height } = element.absoluteBoundingBox;
|
|
2518
|
+
if (typeof x !== "number" || typeof y !== "number" || typeof width !== "number" || typeof height !== "number") {
|
|
2519
|
+
console.warn(
|
|
2520
|
+
`\u26A0\uFE0F absoluteBoundingBox\u7C7B\u578B\u9519\u8BEF: "${element.name}"`,
|
|
2521
|
+
element.absoluteBoundingBox
|
|
2522
|
+
);
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
// 同步布局相关属性
|
|
2527
|
+
syncLayoutProperties(element, parentElement) {
|
|
2528
|
+
if (!parentElement) {
|
|
2529
|
+
element.layoutMode = element.layoutMode || "NONE";
|
|
2530
|
+
element.layoutGrow = 0;
|
|
2531
|
+
element.layoutSizingHorizontal = "FIXED";
|
|
2532
|
+
element.layoutSizingVertical = "FIXED";
|
|
2533
|
+
element.primaryAxisAlignItems = "MIN";
|
|
2534
|
+
element.counterAxisAlignItems = "MIN";
|
|
2535
|
+
return;
|
|
2536
|
+
}
|
|
2537
|
+
if (parentElement.layoutMode && parentElement.layoutMode !== "NONE") {
|
|
2538
|
+
element.layoutGrow = element.layoutGrow || 0;
|
|
2539
|
+
element.layoutSizingHorizontal = element.layoutSizingHorizontal || "FIXED";
|
|
2540
|
+
element.layoutSizingVertical = element.layoutSizingVertical || "FIXED";
|
|
2541
|
+
if (element.layoutPositioning === "ABSOLUTE") {
|
|
2542
|
+
delete element.layoutPositioning;
|
|
2543
|
+
}
|
|
2544
|
+
} else {
|
|
2545
|
+
if (this.needsAbsolutePositioning(element, parentElement)) {
|
|
2546
|
+
element.layoutPositioning = "ABSOLUTE";
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
},
|
|
2550
|
+
// 同步约束属性
|
|
2551
|
+
syncConstraints(element, parentElement) {
|
|
2552
|
+
if (!element.constraints) {
|
|
2553
|
+
element.constraints = {
|
|
2554
|
+
vertical: "TOP",
|
|
2555
|
+
horizontal: "LEFT"
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
if (element.layoutPositioning === "ABSOLUTE") {
|
|
2559
|
+
element.constraints.vertical = "TOP";
|
|
2560
|
+
element.constraints.horizontal = "LEFT";
|
|
2561
|
+
}
|
|
2562
|
+
},
|
|
2563
|
+
// 确保层级健康性 - 精确处理所有关键属性
|
|
2564
|
+
ensureHierarchyHealth(rootElement) {
|
|
2565
|
+
let healthIssues = 0;
|
|
2566
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2567
|
+
const checkRecursive = (element, parentElement = null, level = 0) => {
|
|
2568
|
+
if (!element || visited.has(element.id)) return;
|
|
2569
|
+
visited.add(element.id);
|
|
2570
|
+
if (!element.id) {
|
|
2571
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11ID: "${element.name}"`);
|
|
2572
|
+
healthIssues++;
|
|
2573
|
+
}
|
|
2574
|
+
if (!element.absoluteBoundingBox) {
|
|
2575
|
+
console.warn(`\u26A0\uFE0F \u5143\u7D20\u7F3A\u5C11absoluteBoundingBox: "${element.name}"`);
|
|
2576
|
+
healthIssues++;
|
|
2577
|
+
} else {
|
|
2578
|
+
const { x, y, width, height } = element.absoluteBoundingBox;
|
|
2579
|
+
if (x === void 0 || y === void 0 || width === void 0 || height === void 0) {
|
|
2580
|
+
console.warn(
|
|
2581
|
+
`\u26A0\uFE0F absoluteBoundingBox\u4E0D\u5B8C\u6574: "${element.name}"`,
|
|
2582
|
+
element.absoluteBoundingBox
|
|
2583
|
+
);
|
|
2584
|
+
healthIssues++;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
if (!element.absoluteRenderBounds) {
|
|
2588
|
+
if (element.absoluteBoundingBox) {
|
|
2589
|
+
element.absoluteRenderBounds = __spreadValues({}, element.absoluteBoundingBox);
|
|
2590
|
+
console.log(`\u{1F527} \u4FEE\u590DabsoluteRenderBounds: "${element.name}"`);
|
|
2591
|
+
healthIssues++;
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
if (parentElement && element.parent !== parentElement) {
|
|
2595
|
+
element.parent = parentElement;
|
|
2596
|
+
healthIssues++;
|
|
2597
|
+
console.log(`\u{1F527} \u4FEE\u590D\u7236\u5B50\u5173\u7CFB: "${element.name}" \u2192 "${parentElement.name}"`);
|
|
2598
|
+
}
|
|
2599
|
+
if (element.children) {
|
|
2600
|
+
for (let i = element.children.length - 1; i >= 0; i--) {
|
|
2601
|
+
const child = element.children[i];
|
|
2602
|
+
if (!child || typeof child !== "object") {
|
|
2603
|
+
element.children.splice(i, 1);
|
|
2604
|
+
healthIssues++;
|
|
2605
|
+
console.log(`\u{1F527} \u79FB\u9664\u65E0\u6548\u5B50\u5143\u7D20: "${element.name}"`);
|
|
2606
|
+
continue;
|
|
2607
|
+
}
|
|
2608
|
+
if (child.parent !== element) {
|
|
2609
|
+
child.parent = element;
|
|
2610
|
+
healthIssues++;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
if (element.x !== void 0 && element.y !== void 0) {
|
|
2615
|
+
if (isNaN(element.x) || isNaN(element.y)) {
|
|
2616
|
+
element.x = 0;
|
|
2617
|
+
element.y = 0;
|
|
2618
|
+
healthIssues++;
|
|
2619
|
+
console.log(`\u{1F527} \u4FEE\u590D\u65E0\u6548\u5750\u6807: "${element.name}"`);
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
if (element.width !== void 0 && element.height !== void 0) {
|
|
2623
|
+
if (isNaN(element.width) || isNaN(element.height) || element.width < 0 || element.height < 0) {
|
|
2624
|
+
if (element.absoluteBoundingBox) {
|
|
2625
|
+
element.width = element.absoluteBoundingBox.width;
|
|
2626
|
+
element.height = element.absoluteBoundingBox.height;
|
|
2627
|
+
} else {
|
|
2628
|
+
element.width = Math.max(element.width || 0, 0);
|
|
2629
|
+
element.height = Math.max(element.height || 0, 0);
|
|
2630
|
+
}
|
|
2631
|
+
healthIssues++;
|
|
2632
|
+
console.log(`\u{1F527} \u4FEE\u590D\u65E0\u6548\u5C3A\u5BF8: "${element.name}"`);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
if (element.layoutMode === void 0) {
|
|
2636
|
+
element.layoutMode = "NONE";
|
|
2637
|
+
console.log(`\u{1F527} \u8BBE\u7F6E\u9ED8\u8BA4layoutMode: "${element.name}"`);
|
|
2638
|
+
healthIssues++;
|
|
2639
|
+
}
|
|
2640
|
+
this.validateRotationProperties(element);
|
|
2641
|
+
this.validateElementProperties(element);
|
|
2642
|
+
this.ensureCorrectType(element, parentElement);
|
|
2643
|
+
if (element.children && element.children.length > 0) {
|
|
2644
|
+
for (const child of element.children) {
|
|
2645
|
+
checkRecursive(child, element, level + 1);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
};
|
|
2649
|
+
checkRecursive(rootElement);
|
|
2650
|
+
if (healthIssues === 0) {
|
|
2651
|
+
console.log("\u2705 \u5C42\u7EA7\u7ED3\u6784\u5B8C\u5168\u5065\u5EB7");
|
|
2652
|
+
} else {
|
|
2653
|
+
console.log(`\u26A0\uFE0F \u53D1\u73B0 ${healthIssues} \u4E2A\u5065\u5EB7\u95EE\u9898\uFF0C\u5DF2\u4FEE\u590D`);
|
|
2654
|
+
}
|
|
2655
|
+
},
|
|
2656
|
+
// 验证布局属性一致性
|
|
2657
|
+
validateLayoutConsistency(element, parentElement) {
|
|
2658
|
+
if (!parentElement) return;
|
|
2659
|
+
if (parentElement.layoutMode && parentElement.layoutMode !== "NONE") {
|
|
2660
|
+
if (element.layoutPositioning === "ABSOLUTE") {
|
|
2661
|
+
delete element.layoutPositioning;
|
|
2662
|
+
console.log(`\u{1F527} \u79FB\u9664\u4E0D\u4E00\u81F4\u7684\u7EDD\u5BF9\u5B9A\u4F4D: "${element.name}"`);
|
|
2663
|
+
}
|
|
2664
|
+
} else {
|
|
2665
|
+
if (this.needsAbsolutePositioning(element, parentElement) && !element.layoutPositioning) {
|
|
2666
|
+
element.layoutPositioning = "ABSOLUTE";
|
|
2667
|
+
console.log(`\u{1F527} \u6DFB\u52A0\u7F3A\u5931\u7684\u7EDD\u5BF9\u5B9A\u4F4D: "${element.name}"`);
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
},
|
|
2671
|
+
// 验证旋转属性 - 确保rotation和cumulativeRotation正确
|
|
2672
|
+
validateRotationProperties(element) {
|
|
2673
|
+
if (!element) return;
|
|
2674
|
+
if (element.rotation !== void 0 && isNaN(element.rotation)) {
|
|
2675
|
+
element.rotation = 0;
|
|
2676
|
+
console.warn(`\u26A0\uFE0F \u4FEE\u590D\u65E0\u6548rotation: "${element.name}"`);
|
|
2677
|
+
}
|
|
2678
|
+
if (element.cumulativeRotation !== void 0 && isNaN(element.cumulativeRotation)) {
|
|
2679
|
+
element.cumulativeRotation = 0;
|
|
2680
|
+
console.warn(`\u26A0\uFE0F \u4FEE\u590D\u65E0\u6548cumulativeRotation: "${element.name}"`);
|
|
2681
|
+
}
|
|
2682
|
+
const totalRotation = (element.rotation || 0) + (element.cumulativeRotation || 0);
|
|
2683
|
+
if (Math.abs(totalRotation) > 360) {
|
|
2684
|
+
console.warn(`\u26A0\uFE0F \u65CB\u8F6C\u89D2\u5EA6\u5F02\u5E38: "${element.name}" totalRotation=${totalRotation}`);
|
|
2685
|
+
}
|
|
2686
|
+
if (element.parent) {
|
|
2687
|
+
let expectedCumulativeRotation = 0;
|
|
2688
|
+
let currentParent = element.parent;
|
|
2689
|
+
while (currentParent) {
|
|
2690
|
+
if (currentParent.rotation !== void 0 && currentParent.rotation !== 0) {
|
|
2691
|
+
expectedCumulativeRotation += currentParent.rotation;
|
|
2692
|
+
}
|
|
2693
|
+
currentParent = currentParent.parent;
|
|
2694
|
+
}
|
|
2695
|
+
if (Math.abs((element.cumulativeRotation || 0) - expectedCumulativeRotation) > 0.1) {
|
|
2696
|
+
const oldCumulativeRotation = element.cumulativeRotation || 0;
|
|
2697
|
+
element.cumulativeRotation = expectedCumulativeRotation;
|
|
2698
|
+
console.log(
|
|
2699
|
+
`\u{1F527} \u4FEE\u590DcumulativeRotation\u7EE7\u627F: "${element.name}" ${oldCumulativeRotation} \u2192 ${expectedCumulativeRotation}`
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
},
|
|
2704
|
+
// 主处理函数
|
|
2705
|
+
reorganize(rootElement) {
|
|
2706
|
+
this.init();
|
|
2707
|
+
if (!rootElement) {
|
|
2708
|
+
console.log("\u274C \u6CA1\u6709\u6839\u5143\u7D20\u53EF\u5904\u7406");
|
|
2709
|
+
return rootElement;
|
|
2710
|
+
}
|
|
2711
|
+
console.log(`\u{1F3AF} \u5F00\u59CB\u91CD\u7EC4\u5C42\u7EA7\u7ED3\u6784: "${rootElement.name}"`);
|
|
2712
|
+
this.processHierarchy(rootElement);
|
|
2713
|
+
console.log("\n\u{1F504} \u5F00\u59CB\u91CD\u65B0\u8BA1\u7B97\u6240\u6709\u5143\u7D20\u5750\u6807...");
|
|
2714
|
+
this.recalculateAllCoordinates(rootElement);
|
|
2715
|
+
console.log("\n\u{1F517} \u540C\u6B65\u7236\u5B50\u5143\u7D20\u5C5E\u6027...");
|
|
2716
|
+
this.syncParentChildProperties(rootElement);
|
|
2717
|
+
console.log("\n\u{1F3E5} \u68C0\u67E5\u5C42\u7EA7\u5065\u5EB7\u6027...");
|
|
2718
|
+
this.ensureHierarchyHealth(rootElement);
|
|
2719
|
+
this.printStats();
|
|
2720
|
+
return rootElement;
|
|
2721
|
+
},
|
|
2722
|
+
// 打印统计信息
|
|
2723
|
+
printStats() {
|
|
2724
|
+
const duration = Date.now() - this.stats.startTime;
|
|
2725
|
+
console.log("\n\u{1F4C8} \u5C42\u7EA7\u91CD\u7EC4\u7EDF\u8BA1:");
|
|
2726
|
+
console.log(` \u603B\u5143\u7D20\u6570: ${this.stats.totalElements}`);
|
|
2727
|
+
console.log(` \u8FC1\u79FB\u5143\u7D20: ${this.stats.migratedElements}`);
|
|
2728
|
+
console.log(` \u79FB\u9664\u91CD\u590D: ${this.stats.removedDuplicates}`);
|
|
2729
|
+
console.log(` \u4FDD\u7559\u5143\u7D20: ${this.stats.preservedElements}`);
|
|
2730
|
+
console.log(` \u5904\u7406\u65F6\u95F4: ${duration}ms`);
|
|
2731
|
+
console.log("\u2705 \u5C42\u7EA7\u91CD\u7EC4\u5B8C\u6210!\n");
|
|
2732
|
+
}
|
|
2733
|
+
};
|
|
2734
|
+
console.log("\u{1F680} \u5F00\u59CB\u6267\u884C\u9AD8\u7EA7\u5C42\u7EA7\u91CD\u7EC4...");
|
|
2735
|
+
|
|
2736
|
+
// ../../packages/pluginMain/src/altNodes/jsonNodeConversion.ts
|
|
2737
|
+
var getNodeByIdAsyncTime = 0;
|
|
2738
|
+
var getNodeByIdAsyncCalls = 0;
|
|
2739
|
+
var getStyledTextSegmentsTime = 0;
|
|
2740
|
+
var getStyledTextSegmentsCalls = 0;
|
|
2741
|
+
var processColorVariablesTime = 0;
|
|
2742
|
+
var processColorVariablesCalls = 0;
|
|
2743
|
+
var resetPerformanceCounters = () => {
|
|
2744
|
+
getNodeByIdAsyncTime = 0;
|
|
2745
|
+
getNodeByIdAsyncCalls = 0;
|
|
2746
|
+
getStyledTextSegmentsTime = 0;
|
|
2747
|
+
getStyledTextSegmentsCalls = 0;
|
|
2748
|
+
processColorVariablesTime = 0;
|
|
2749
|
+
processColorVariablesCalls = 0;
|
|
2750
|
+
};
|
|
2751
|
+
var nodeNameCounters = /* @__PURE__ */ new Map();
|
|
2752
|
+
var variableCache = /* @__PURE__ */ new Map();
|
|
2753
|
+
var memoizedVariableToColorName = (variableId) => __async(null, null, function* () {
|
|
2754
|
+
if (!variableCache.has(variableId)) {
|
|
2755
|
+
const colorName = (yield variableToColorName(variableId)).replaceAll(",", "");
|
|
2756
|
+
variableCache.set(variableId, colorName);
|
|
2757
|
+
return colorName;
|
|
2758
|
+
}
|
|
2759
|
+
return variableCache.get(variableId);
|
|
2760
|
+
});
|
|
2761
|
+
var collectNodeColorVariables = (node) => __async(null, null, function* () {
|
|
2762
|
+
const colorMappings = /* @__PURE__ */ new Map();
|
|
2763
|
+
const addMappingFromPaint = (paint) => {
|
|
2764
|
+
var _a;
|
|
2765
|
+
if (paint.type === "SOLID" && paint.variableColorName && paint.color && ((_a = paint.boundVariables) == null ? void 0 : _a.color)) {
|
|
2766
|
+
const variableName = paint.boundVariables.color.name || paint.variableColorName;
|
|
2767
|
+
if (variableName) {
|
|
2768
|
+
const sanitizedVarName = variableName.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
2769
|
+
const colorInfo = {
|
|
2770
|
+
variableId: paint.boundVariables.color.id,
|
|
2771
|
+
variableName: sanitizedVarName
|
|
2772
|
+
};
|
|
2773
|
+
const r = Math.round(paint.color.r * 255);
|
|
2774
|
+
const g = Math.round(paint.color.g * 255);
|
|
2775
|
+
const b = Math.round(paint.color.b * 255);
|
|
2776
|
+
const hexColor = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`.toLowerCase();
|
|
2777
|
+
colorMappings.set(hexColor, colorInfo);
|
|
2778
|
+
if (r === 255 && g === 255 && b === 255) {
|
|
2779
|
+
colorMappings.set("white", colorInfo);
|
|
2780
|
+
colorMappings.set("rgb(255,255,255)", colorInfo);
|
|
2781
|
+
} else if (r === 0 && g === 0 && b === 0) {
|
|
2782
|
+
colorMappings.set("black", colorInfo);
|
|
2783
|
+
colorMappings.set("rgb(0,0,0)", colorInfo);
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
if (node.fills && Array.isArray(node.fills)) {
|
|
2789
|
+
node.fills.forEach(addMappingFromPaint);
|
|
2790
|
+
}
|
|
2791
|
+
if (node.strokes && Array.isArray(node.strokes)) {
|
|
2792
|
+
node.strokes.forEach(addMappingFromPaint);
|
|
2793
|
+
}
|
|
2794
|
+
if (node.children && Array.isArray(node.children)) {
|
|
2795
|
+
for (const child of node.children) {
|
|
2796
|
+
const childMappings = yield collectNodeColorVariables(child);
|
|
2797
|
+
childMappings.forEach((value, key) => {
|
|
2798
|
+
colorMappings.set(key, value);
|
|
2799
|
+
});
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return colorMappings;
|
|
2803
|
+
});
|
|
2804
|
+
var processColorVariables = (paint) => __async(null, null, function* () {
|
|
2805
|
+
var _a;
|
|
2806
|
+
const start = Date.now();
|
|
2807
|
+
processColorVariablesCalls++;
|
|
2808
|
+
if (paint.type === "GRADIENT_ANGULAR" || paint.type === "GRADIENT_DIAMOND" || paint.type === "GRADIENT_LINEAR" || paint.type === "GRADIENT_RADIAL") {
|
|
2809
|
+
const stopsWithVariables = paint.gradientStops.filter((stop) => {
|
|
2810
|
+
var _a2;
|
|
2811
|
+
return (_a2 = stop.boundVariables) == null ? void 0 : _a2.color;
|
|
2812
|
+
});
|
|
2813
|
+
if (stopsWithVariables.length > 0) {
|
|
2814
|
+
yield Promise.all(
|
|
2815
|
+
stopsWithVariables.map((stop) => __async(null, null, function* () {
|
|
2816
|
+
;
|
|
2817
|
+
stop.variableColorName = yield memoizedVariableToColorName(
|
|
2818
|
+
stop.boundVariables.color.id
|
|
2819
|
+
);
|
|
2820
|
+
}))
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
} else if (paint.type === "SOLID" && ((_a = paint.boundVariables) == null ? void 0 : _a.color)) {
|
|
2824
|
+
;
|
|
2825
|
+
paint.variableColorName = yield memoizedVariableToColorName(
|
|
2826
|
+
paint.boundVariables.color.id
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
processColorVariablesTime += Date.now() - start;
|
|
2830
|
+
});
|
|
2831
|
+
var processEffectVariables = (paint) => __async(null, null, function* () {
|
|
2832
|
+
var _a;
|
|
2833
|
+
const start = Date.now();
|
|
2834
|
+
processColorVariablesCalls++;
|
|
2835
|
+
if ((_a = paint.boundVariables) == null ? void 0 : _a.color) {
|
|
2836
|
+
;
|
|
2837
|
+
paint.variableColorName = yield memoizedVariableToColorName(
|
|
2838
|
+
paint.boundVariables.color.id
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
processColorVariablesTime += Date.now() - start;
|
|
2842
|
+
});
|
|
2843
|
+
var getColorVariables = (node, settings) => __async(null, null, function* () {
|
|
2844
|
+
if (settings.useColorVariables) {
|
|
2845
|
+
if (node.fills && Array.isArray(node.fills)) {
|
|
2846
|
+
yield Promise.all(node.fills.map((fill) => processColorVariables(fill)));
|
|
2847
|
+
}
|
|
2848
|
+
if (node.strokes && Array.isArray(node.strokes)) {
|
|
2849
|
+
yield Promise.all(node.strokes.map((stroke) => processColorVariables(stroke)));
|
|
2850
|
+
}
|
|
2851
|
+
if ("effects" in node && node.effects && Array.isArray(node.effects)) {
|
|
2852
|
+
yield Promise.all(
|
|
2853
|
+
node.effects.filter(
|
|
2854
|
+
(effect) => effect.type === "DROP_SHADOW" || effect.type === "INNER_SHADOW"
|
|
2855
|
+
).map((effect) => processEffectVariables(effect))
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
});
|
|
2860
|
+
function adjustChildrenOrder(node) {
|
|
2861
|
+
if (!node.itemReverseZIndex || !node.children || node.layoutMode === "NONE") {
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
const children = node.children;
|
|
2865
|
+
const absoluteChildren = [];
|
|
2866
|
+
const fixedChildren = [];
|
|
2867
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
2868
|
+
const child = children[i];
|
|
2869
|
+
if (child.layoutPositioning === "ABSOLUTE") {
|
|
2870
|
+
absoluteChildren.push(child);
|
|
2871
|
+
} else {
|
|
2872
|
+
fixedChildren.unshift(child);
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
node.children = [...absoluteChildren, ...fixedChildren];
|
|
2876
|
+
}
|
|
2877
|
+
var getParentBBoxOrigin = (parent) => {
|
|
2878
|
+
if (parent && "absoluteBoundingBox" in parent && parent.absoluteBoundingBox) {
|
|
2879
|
+
return { x: parent.absoluteBoundingBox.x, y: parent.absoluteBoundingBox.y };
|
|
2880
|
+
}
|
|
2881
|
+
return { x: 0, y: 0 };
|
|
2882
|
+
};
|
|
2883
|
+
function toProcessingAltNode(node) {
|
|
2884
|
+
return node;
|
|
2885
|
+
}
|
|
2886
|
+
var processNodePair = (jsonNode, figmaNode, settings, parentNode, parentCumulativeRotation = 0) => __async(null, null, function* () {
|
|
2887
|
+
var _a, _b;
|
|
2888
|
+
if (!jsonNode.id) return null;
|
|
2889
|
+
if (jsonNode.visible === false) return null;
|
|
2890
|
+
const nodeType = jsonNode.type;
|
|
2891
|
+
if (parentNode) {
|
|
2892
|
+
jsonNode.cumulativeRotation = parentCumulativeRotation;
|
|
2893
|
+
}
|
|
2894
|
+
if ((nodeType === "FRAME" || nodeType === "INSTANCE" || nodeType === "COMPONENT" || nodeType === "COMPONENT_SET") && (!jsonNode.children || jsonNode.children.length === 0)) {
|
|
2895
|
+
;
|
|
2896
|
+
jsonNode.type = "RECTANGLE";
|
|
2897
|
+
return processNodePair(jsonNode, figmaNode, settings, parentNode, parentCumulativeRotation);
|
|
2898
|
+
}
|
|
2899
|
+
if ("rotation" in jsonNode && jsonNode.rotation) {
|
|
2900
|
+
jsonNode.rotation = -jsonNode.rotation * (180 / Math.PI);
|
|
2901
|
+
}
|
|
2902
|
+
if (nodeType === "GROUP" && jsonNode.children) {
|
|
2903
|
+
const processedChildren = [];
|
|
2904
|
+
if (Array.isArray(jsonNode.children) && figmaNode && "children" in figmaNode) {
|
|
2905
|
+
const visibleJsonChildren = jsonNode.children.filter((child) => child.visible !== false);
|
|
2906
|
+
const figmaChildrenById = /* @__PURE__ */ new Map();
|
|
2907
|
+
figmaNode.children.forEach((child) => {
|
|
2908
|
+
figmaChildrenById.set(child.id, child);
|
|
2909
|
+
});
|
|
2910
|
+
for (const child of visibleJsonChildren) {
|
|
2911
|
+
const figmaChild = figmaChildrenById.get(child.id);
|
|
2912
|
+
if (!figmaChild) continue;
|
|
2913
|
+
const processedChild = yield processNodePair(
|
|
2914
|
+
toProcessingAltNode(child),
|
|
2915
|
+
figmaChild,
|
|
2916
|
+
settings,
|
|
2917
|
+
parentNode,
|
|
2918
|
+
// The group's parent
|
|
2919
|
+
parentCumulativeRotation + (jsonNode.rotation || 0)
|
|
2920
|
+
);
|
|
2921
|
+
if (processedChild !== null) {
|
|
2922
|
+
if (Array.isArray(processedChild)) {
|
|
2923
|
+
processedChildren.push(...processedChild);
|
|
2924
|
+
} else {
|
|
2925
|
+
processedChildren.push(processedChild);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
return processedChildren;
|
|
2931
|
+
}
|
|
2932
|
+
if (nodeType === "SLICE") {
|
|
2933
|
+
return null;
|
|
2934
|
+
}
|
|
2935
|
+
if (parentNode) {
|
|
2936
|
+
jsonNode.parent = parentNode;
|
|
2937
|
+
}
|
|
2938
|
+
const cleanName = jsonNode.name.trim();
|
|
2939
|
+
const count = nodeNameCounters.get(cleanName) || 0;
|
|
2940
|
+
nodeNameCounters.set(cleanName, count + 1);
|
|
2941
|
+
jsonNode.uniqueName = count === 0 ? cleanName : `${cleanName}_${count.toString().padStart(2, "0")}`;
|
|
2942
|
+
if (figmaNode.type === "TEXT") {
|
|
2943
|
+
const getSegmentsStart = Date.now();
|
|
2944
|
+
getStyledTextSegmentsCalls++;
|
|
2945
|
+
let styledTextSegments = figmaNode.getStyledTextSegments([
|
|
2946
|
+
"fontName",
|
|
2947
|
+
"fills",
|
|
2948
|
+
"fontSize",
|
|
2949
|
+
"fontWeight",
|
|
2950
|
+
"hyperlink",
|
|
2951
|
+
"indentation",
|
|
2952
|
+
"letterSpacing",
|
|
2953
|
+
"lineHeight",
|
|
2954
|
+
"listOptions",
|
|
2955
|
+
"textCase",
|
|
2956
|
+
"textDecoration",
|
|
2957
|
+
"textStyleId",
|
|
2958
|
+
"fillStyleId",
|
|
2959
|
+
"openTypeFeatures"
|
|
2960
|
+
]);
|
|
2961
|
+
getStyledTextSegmentsTime += Date.now() - getSegmentsStart;
|
|
2962
|
+
if (styledTextSegments.length > 0) {
|
|
2963
|
+
const baseSegmentName = (jsonNode.uniqueName || jsonNode.name).replace(/[^a-zA-Z0-9_-]/g, "").toLowerCase();
|
|
2964
|
+
styledTextSegments = yield Promise.all(
|
|
2965
|
+
styledTextSegments.map((segment, index) => __async(null, null, function* () {
|
|
2966
|
+
const mutableSegment = Object.assign({}, segment);
|
|
2967
|
+
if (settings.useColorVariables && segment.fills) {
|
|
2968
|
+
mutableSegment.fills = yield Promise.all(
|
|
2969
|
+
segment.fills.map((d) => __async(null, null, function* () {
|
|
2970
|
+
if (d.blendMode !== "PASS_THROUGH" && d.blendMode !== "NORMAL") {
|
|
2971
|
+
addWarning("BlendMode is not supported in Text colors");
|
|
2972
|
+
}
|
|
2973
|
+
const fill = __spreadValues({}, d);
|
|
2974
|
+
yield processColorVariables(fill);
|
|
2975
|
+
return fill;
|
|
2976
|
+
}))
|
|
2977
|
+
);
|
|
2978
|
+
}
|
|
2979
|
+
if (styledTextSegments.length === 1) {
|
|
2980
|
+
mutableSegment.uniqueId = `${baseSegmentName}_span`;
|
|
2981
|
+
} else {
|
|
2982
|
+
mutableSegment.uniqueId = `${baseSegmentName}_span_${(index + 1).toString().padStart(2, "0")}`;
|
|
2983
|
+
}
|
|
2984
|
+
return mutableSegment;
|
|
2985
|
+
}))
|
|
2986
|
+
);
|
|
2987
|
+
jsonNode.styledTextSegments = styledTextSegments;
|
|
2988
|
+
}
|
|
2989
|
+
if (jsonNode.style) {
|
|
2990
|
+
Object.assign(jsonNode, jsonNode.style);
|
|
2991
|
+
}
|
|
2992
|
+
if (!jsonNode.textAutoResize) {
|
|
2993
|
+
jsonNode.textAutoResize = "NONE";
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
if ("absoluteBoundingBox" in jsonNode && jsonNode.absoluteBoundingBox) {
|
|
2997
|
+
if (jsonNode.parent) {
|
|
2998
|
+
const parentOrigin = getParentBBoxOrigin(jsonNode.parent);
|
|
2999
|
+
const rect = calculateRectangleFromBoundingBox(
|
|
3000
|
+
{
|
|
3001
|
+
width: jsonNode.absoluteBoundingBox.width,
|
|
3002
|
+
height: jsonNode.absoluteBoundingBox.height,
|
|
3003
|
+
x: jsonNode.absoluteBoundingBox.x - parentOrigin.x,
|
|
3004
|
+
y: jsonNode.absoluteBoundingBox.y - parentOrigin.y
|
|
3005
|
+
},
|
|
3006
|
+
-((jsonNode.rotation || 0) + (jsonNode.cumulativeRotation || 0))
|
|
3007
|
+
);
|
|
3008
|
+
jsonNode.width = rect.width;
|
|
3009
|
+
jsonNode.height = rect.height;
|
|
3010
|
+
jsonNode.x = rect.left;
|
|
3011
|
+
jsonNode.y = rect.top;
|
|
3012
|
+
} else {
|
|
3013
|
+
jsonNode.width = jsonNode.absoluteBoundingBox.width;
|
|
3014
|
+
jsonNode.height = jsonNode.absoluteBoundingBox.height;
|
|
3015
|
+
jsonNode.x = 0;
|
|
3016
|
+
jsonNode.y = 0;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
if (settings.embedVectors && !(parentNode == null ? void 0 : parentNode.canBeFlattened)) {
|
|
3020
|
+
const isIcon = isLikelyIcon(jsonNode);
|
|
3021
|
+
jsonNode.canBeFlattened = isIcon;
|
|
3022
|
+
if (isIcon && settings.useColorVariables) {
|
|
3023
|
+
;
|
|
3024
|
+
jsonNode._collectColorMappings = true;
|
|
3025
|
+
}
|
|
3026
|
+
} else {
|
|
3027
|
+
;
|
|
3028
|
+
jsonNode.canBeFlattened = false;
|
|
3029
|
+
}
|
|
3030
|
+
if ("individualStrokeWeights" in jsonNode && jsonNode.individualStrokeWeights) {
|
|
3031
|
+
;
|
|
3032
|
+
jsonNode.strokeTopWeight = jsonNode.individualStrokeWeights.top;
|
|
3033
|
+
jsonNode.strokeBottomWeight = jsonNode.individualStrokeWeights.bottom;
|
|
3034
|
+
jsonNode.strokeLeftWeight = jsonNode.individualStrokeWeights.left;
|
|
3035
|
+
jsonNode.strokeRightWeight = jsonNode.individualStrokeWeights.right;
|
|
3036
|
+
}
|
|
3037
|
+
if ("fills" in jsonNode) {
|
|
3038
|
+
yield getColorVariables(jsonNode, settings);
|
|
3039
|
+
}
|
|
3040
|
+
if ("layoutMode" in jsonNode && jsonNode.layoutMode) {
|
|
3041
|
+
if (jsonNode.paddingLeft === void 0) {
|
|
3042
|
+
jsonNode.paddingLeft = 0;
|
|
3043
|
+
}
|
|
3044
|
+
if (jsonNode.paddingRight === void 0) {
|
|
3045
|
+
jsonNode.paddingRight = 0;
|
|
3046
|
+
}
|
|
3047
|
+
if (jsonNode.paddingTop === void 0) {
|
|
3048
|
+
jsonNode.paddingTop = 0;
|
|
3049
|
+
}
|
|
3050
|
+
if (jsonNode.paddingBottom === void 0) {
|
|
3051
|
+
jsonNode.paddingBottom = 0;
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
if (!jsonNode.layoutMode) jsonNode.layoutMode = "NONE";
|
|
3055
|
+
if (!jsonNode.layoutGrow) jsonNode.layoutGrow = 0;
|
|
3056
|
+
if (!jsonNode.layoutSizingHorizontal) jsonNode.layoutSizingHorizontal = "FIXED";
|
|
3057
|
+
if (!jsonNode.layoutSizingVertical) jsonNode.layoutSizingVertical = "FIXED";
|
|
3058
|
+
if (!jsonNode.primaryAxisAlignItems) {
|
|
3059
|
+
jsonNode.primaryAxisAlignItems = "MIN";
|
|
3060
|
+
}
|
|
3061
|
+
if (!jsonNode.counterAxisAlignItems) {
|
|
3062
|
+
jsonNode.counterAxisAlignItems = "MIN";
|
|
3063
|
+
}
|
|
3064
|
+
const hasChildren = "children" in jsonNode && jsonNode.children && Array.isArray(jsonNode.children) && jsonNode.children.length > 0;
|
|
3065
|
+
if (jsonNode.layoutSizingHorizontal === "HUG" && !hasChildren) {
|
|
3066
|
+
jsonNode.layoutSizingHorizontal = "FIXED";
|
|
3067
|
+
}
|
|
3068
|
+
if (jsonNode.layoutSizingVertical === "HUG" && !hasChildren) {
|
|
3069
|
+
jsonNode.layoutSizingVertical = "FIXED";
|
|
3070
|
+
}
|
|
3071
|
+
if ("children" in jsonNode && jsonNode.children && Array.isArray(jsonNode.children) && "children" in figmaNode) {
|
|
3072
|
+
const visibleJsonChildren = jsonNode.children.filter((child) => child.visible !== false);
|
|
3073
|
+
const figmaChildrenById = /* @__PURE__ */ new Map();
|
|
3074
|
+
figmaNode.children.forEach((child) => {
|
|
3075
|
+
figmaChildrenById.set(child.id, child);
|
|
3076
|
+
});
|
|
3077
|
+
const cumulative = parentCumulativeRotation + (jsonNode.type === "GROUP" ? jsonNode.rotation || 0 : 0);
|
|
3078
|
+
const processedChildren = [];
|
|
3079
|
+
for (const child of visibleJsonChildren) {
|
|
3080
|
+
const figmaChild = figmaChildrenById.get(child.id);
|
|
3081
|
+
if (!figmaChild) continue;
|
|
3082
|
+
const processedChild = yield processNodePair(
|
|
3083
|
+
toProcessingAltNode(child),
|
|
3084
|
+
figmaChild,
|
|
3085
|
+
settings,
|
|
3086
|
+
jsonNode,
|
|
3087
|
+
cumulative
|
|
3088
|
+
);
|
|
3089
|
+
if (processedChild !== null) {
|
|
3090
|
+
if (Array.isArray(processedChild)) {
|
|
3091
|
+
processedChildren.push(...processedChild);
|
|
3092
|
+
} else {
|
|
3093
|
+
processedChildren.push(processedChild);
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
;
|
|
3098
|
+
jsonNode.children = processedChildren;
|
|
3099
|
+
if (jsonNode.layoutMode === "NONE" || ((_b = (_a = jsonNode.children) == null ? void 0 : _a.some(
|
|
3100
|
+
(d) => "layoutPositioning" in d && d.layoutPositioning === "ABSOLUTE"
|
|
3101
|
+
)) != null ? _b : false)) {
|
|
3102
|
+
jsonNode.isRelative = true;
|
|
3103
|
+
}
|
|
3104
|
+
adjustChildrenOrder(jsonNode);
|
|
3105
|
+
}
|
|
3106
|
+
if (jsonNode._collectColorMappings) {
|
|
3107
|
+
;
|
|
3108
|
+
jsonNode.colorVariableMappings = yield collectNodeColorVariables(jsonNode);
|
|
3109
|
+
delete jsonNode._collectColorMappings;
|
|
3110
|
+
}
|
|
3111
|
+
return jsonNode;
|
|
3112
|
+
});
|
|
3113
|
+
var nodesToJSON = (nodes, settings) => __async(null, null, function* () {
|
|
3114
|
+
nodeNameCounters.clear();
|
|
3115
|
+
const exportJsonStart = Date.now();
|
|
3116
|
+
console.log("[debug] nodesToJSON - nodes", nodes);
|
|
3117
|
+
const nodeResults = yield Promise.all(
|
|
3118
|
+
nodes.map((node) => __async(null, null, function* () {
|
|
3119
|
+
const nodeDoc = (yield node.exportAsync({
|
|
3120
|
+
format: "JSON_REST_V1"
|
|
3121
|
+
})).document;
|
|
3122
|
+
console.log(
|
|
3123
|
+
"[debug] nodeDoc",
|
|
3124
|
+
node.type,
|
|
3125
|
+
node,
|
|
3126
|
+
nodeDoc,
|
|
3127
|
+
yield node.exportAsync({
|
|
3128
|
+
format: "JSON_REST_V1"
|
|
3129
|
+
})
|
|
3130
|
+
);
|
|
3131
|
+
let nodeCumulativeRotation = 0;
|
|
3132
|
+
if (node.type === "GROUP") {
|
|
3133
|
+
nodeDoc.type = "FRAME";
|
|
3134
|
+
if ("rotation" in nodeDoc && nodeDoc.rotation) {
|
|
3135
|
+
nodeCumulativeRotation = -nodeDoc.rotation * (180 / Math.PI);
|
|
3136
|
+
nodeDoc.rotation = 0;
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
return {
|
|
3140
|
+
nodeDoc,
|
|
3141
|
+
nodeCumulativeRotation
|
|
3142
|
+
};
|
|
3143
|
+
}))
|
|
3144
|
+
);
|
|
3145
|
+
console.log("[debug] initial nodeJson", __spreadValues({}, nodes[0]));
|
|
3146
|
+
console.log(
|
|
3147
|
+
`[benchmark][inside nodesToJSON] JSON_REST_V1 export: ${Date.now() - exportJsonStart}ms`
|
|
3148
|
+
);
|
|
3149
|
+
const processNodesStart = Date.now();
|
|
3150
|
+
const result = [];
|
|
3151
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3152
|
+
const nodeResult = nodeResults[i];
|
|
3153
|
+
const figmaNode = nodes[i];
|
|
3154
|
+
if (!nodeResult || !figmaNode) continue;
|
|
3155
|
+
const processedNode = yield processNodePair(
|
|
3156
|
+
nodeResult.nodeDoc,
|
|
3157
|
+
figmaNode,
|
|
3158
|
+
settings,
|
|
3159
|
+
void 0,
|
|
3160
|
+
nodeResult.nodeCumulativeRotation
|
|
3161
|
+
);
|
|
3162
|
+
if (processedNode !== null) {
|
|
3163
|
+
if (Array.isArray(processedNode)) {
|
|
3164
|
+
result.push(...processedNode);
|
|
3165
|
+
} else {
|
|
3166
|
+
result.push(processedNode);
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
console.log(
|
|
3171
|
+
`[benchmark][inside nodesToJSON] Process node pairs: ${Date.now() - processNodesStart}ms`
|
|
3172
|
+
);
|
|
3173
|
+
console.log("[debug] final nodeJson for result", result);
|
|
3174
|
+
const reorganizedResults = result.map((node) => {
|
|
3175
|
+
console.log(`[debug] \u5F00\u59CB\u91CD\u7EC4\u8282\u70B9: ${node.name}`);
|
|
3176
|
+
return AdvancedHierarchyReorganizer.reorganize(node);
|
|
3177
|
+
});
|
|
3178
|
+
console.log("[debug] reorganizedResults", reorganizedResults);
|
|
3179
|
+
return reorganizedResults;
|
|
3180
|
+
});
|
|
3181
|
+
|
|
3182
|
+
// ../../packages/pluginMain/src/common/retrieveUI/retrieveColors.ts
|
|
3183
|
+
var retrieveGenericSolidUIColors = () => __async(null, null, function* () {
|
|
3184
|
+
const selectionColors = figma.getSelectionColors();
|
|
3185
|
+
if (!selectionColors || selectionColors.paints.length === 0) return [];
|
|
3186
|
+
const colors = [];
|
|
3187
|
+
yield Promise.all(
|
|
3188
|
+
selectionColors.paints.map((d) => __async(null, null, function* () {
|
|
3189
|
+
const paint = __spreadValues({}, d);
|
|
3190
|
+
yield processColorVariables(paint);
|
|
3191
|
+
const fill = convertSolidColor(paint);
|
|
3192
|
+
if (fill) {
|
|
3193
|
+
const exists = colors.find((col) => col.exportValue === fill.exportValue);
|
|
3194
|
+
if (!exists) {
|
|
3195
|
+
colors.push(fill);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}))
|
|
3199
|
+
);
|
|
3200
|
+
return colors.sort((a, b) => a.hex.localeCompare(b.hex));
|
|
3201
|
+
});
|
|
3202
|
+
var convertSolidColor = (fill) => {
|
|
3203
|
+
const black = { r: 0, g: 0, b: 0 };
|
|
3204
|
+
const white = { r: 1, g: 1, b: 1 };
|
|
3205
|
+
if (fill.type !== "SOLID") return null;
|
|
3206
|
+
const output = {
|
|
3207
|
+
hex: rgbTo6hex(fill.color).toUpperCase(),
|
|
3208
|
+
colorName: "",
|
|
3209
|
+
exportValue: htmlColorFromFill(fill),
|
|
3210
|
+
contrastBlack: calculateContrastRatio(fill.color, black),
|
|
3211
|
+
contrastWhite: calculateContrastRatio(fill.color, white)
|
|
3212
|
+
};
|
|
3213
|
+
return output;
|
|
3214
|
+
};
|
|
3215
|
+
var retrieveGenericLinearGradients = () => {
|
|
3216
|
+
var _a, _b;
|
|
3217
|
+
const selectionColors = figma.getSelectionColors();
|
|
3218
|
+
const colorStr = [];
|
|
3219
|
+
if (!selectionColors || selectionColors.paints.length === 0) return Promise.resolve([]);
|
|
3220
|
+
for (const paint of selectionColors.paints) {
|
|
3221
|
+
if (paint.type === "GRADIENT_LINEAR") {
|
|
3222
|
+
const t = paint.gradientTransform;
|
|
3223
|
+
const fill = __spreadProps(__spreadValues({}, paint), {
|
|
3224
|
+
gradientStops: [...paint.gradientStops],
|
|
3225
|
+
gradientHandlePositions: [
|
|
3226
|
+
{ x: t[0][2], y: t[1][2] },
|
|
3227
|
+
{ x: t[0][0] + t[0][2], y: t[1][0] + t[1][2] }
|
|
3228
|
+
],
|
|
3229
|
+
blendMode: (_a = paint.blendMode) != null ? _a : "NORMAL"
|
|
3230
|
+
});
|
|
3231
|
+
if (fill.gradientStops) {
|
|
3232
|
+
for (const stop of fill.gradientStops) {
|
|
3233
|
+
if ((_b = stop.boundVariables) == null ? void 0 : _b.color) {
|
|
3234
|
+
try {
|
|
3235
|
+
const variableId = stop.boundVariables.color.id;
|
|
3236
|
+
const variable = figma.variables.getVariableById(variableId);
|
|
3237
|
+
if (variable) {
|
|
3238
|
+
;
|
|
3239
|
+
stop.variableColorName = variable.name.replace(/\s+/g, "-").toLowerCase();
|
|
3240
|
+
}
|
|
3241
|
+
} catch (e) {
|
|
3242
|
+
console.error("Error retrieving variable for gradient stop:", e);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
colorStr.push({
|
|
3248
|
+
cssPreview: htmlGradientFromFills(fill),
|
|
3249
|
+
exportValue: htmlGradientFromFills(fill)
|
|
3250
|
+
});
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
return Promise.resolve(colorStr);
|
|
3254
|
+
};
|
|
3255
|
+
|
|
3256
|
+
// ../../packages/pluginMain/src/messaging.ts
|
|
3257
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
3258
|
+
var postBackendMessage = (message) => {
|
|
3259
|
+
figma.ui.postMessage(message);
|
|
3260
|
+
};
|
|
3261
|
+
var postEmptyMessage = () => {
|
|
3262
|
+
postBackendMessage({ type: "empty" });
|
|
3263
|
+
};
|
|
3264
|
+
var postConversionComplete = (conversionData) => {
|
|
3265
|
+
postBackendMessage(__spreadProps(__spreadValues({}, conversionData), { type: "code" }));
|
|
3266
|
+
};
|
|
3267
|
+
var postSettingsChanged = (settings) => {
|
|
3268
|
+
postBackendMessage({
|
|
3269
|
+
type: "pluginSettingsChanged",
|
|
3270
|
+
settings
|
|
3271
|
+
});
|
|
3272
|
+
};
|
|
3273
|
+
var postLoadingAsync = (_0, ..._1) => __async(null, [_0, ..._1], function* (loading, options = {}) {
|
|
3274
|
+
const { sleepBefore = 10, sleepAfter = 10 } = options;
|
|
3275
|
+
if (sleepBefore) {
|
|
3276
|
+
yield sleep(sleepBefore);
|
|
3277
|
+
}
|
|
3278
|
+
postBackendMessage({ type: "loading", loading });
|
|
3279
|
+
if (sleepAfter) {
|
|
3280
|
+
yield sleep(sleepAfter);
|
|
3281
|
+
}
|
|
3282
|
+
});
|
|
3283
|
+
|
|
3284
|
+
// ../../packages/pluginMain/src/common/retrieveUI/convertToCode.ts
|
|
3285
|
+
var convertToCode = (nodes, settings) => __async(null, null, function* () {
|
|
3286
|
+
const data = yield htmlMain(nodes, settings);
|
|
3287
|
+
return __spreadProps(__spreadValues({}, data), { base64ImageList: [] });
|
|
3288
|
+
});
|
|
3289
|
+
|
|
3290
|
+
// ../../packages/pluginMain/src/altNodes/oldAltConversion.ts
|
|
3291
|
+
var isTypeOrGroupOfTypes2 = curry((matchTypes, node) => {
|
|
3292
|
+
if (!node.visible || matchTypes.includes(node.type)) return true;
|
|
3293
|
+
if ("children" in node) {
|
|
3294
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
3295
|
+
const childNode = node.children[i];
|
|
3296
|
+
const result = isTypeOrGroupOfTypes2(matchTypes, childNode);
|
|
3297
|
+
if (result) continue;
|
|
3298
|
+
return false;
|
|
3299
|
+
}
|
|
3300
|
+
return true;
|
|
3301
|
+
}
|
|
3302
|
+
return false;
|
|
3303
|
+
});
|
|
3304
|
+
var globalTextStyleSegments = {};
|
|
3305
|
+
var canBeFlattened = isTypeOrGroupOfTypes2(["VECTOR", "STAR", "POLYGON", "BOOLEAN_OPERATION"]);
|
|
3306
|
+
var convertNodeToAltNode = (parent) => (node) => {
|
|
3307
|
+
const type = node.type;
|
|
3308
|
+
switch (type) {
|
|
3309
|
+
// Standard nodes
|
|
3310
|
+
case "RECTANGLE":
|
|
3311
|
+
case "ELLIPSE":
|
|
3312
|
+
case "LINE":
|
|
3313
|
+
case "STAR":
|
|
3314
|
+
case "POLYGON":
|
|
3315
|
+
case "VECTOR":
|
|
3316
|
+
case "BOOLEAN_OPERATION":
|
|
3317
|
+
return cloneNode(node, parent);
|
|
3318
|
+
// Group nodes
|
|
3319
|
+
case "FRAME":
|
|
3320
|
+
case "INSTANCE":
|
|
3321
|
+
case "COMPONENT":
|
|
3322
|
+
case "COMPONENT_SET":
|
|
3323
|
+
if (node.children.length === 0) return cloneAsRectangleNode(node, parent);
|
|
3324
|
+
// goto SECTION
|
|
3325
|
+
case "GROUP":
|
|
3326
|
+
if (type === "GROUP" && node.children.length === 1 && node.visible) {
|
|
3327
|
+
const child = node.children[0];
|
|
3328
|
+
if (child) return convertNodeToAltNode(parent)(child);
|
|
3329
|
+
}
|
|
3330
|
+
// goto SECTION
|
|
3331
|
+
case "SECTION":
|
|
3332
|
+
const group = cloneNode(node, parent);
|
|
3333
|
+
const groupChildren = oldConvertNodesToAltNodes(node.children, group);
|
|
3334
|
+
return assignChildren(groupChildren, group);
|
|
3335
|
+
// Text Nodes
|
|
3336
|
+
case "TEXT":
|
|
3337
|
+
globalTextStyleSegments[node.id] = extractStyledTextSegments(
|
|
3338
|
+
node
|
|
3339
|
+
);
|
|
3340
|
+
return cloneNode(node, parent);
|
|
3341
|
+
// Unsupported Nodes
|
|
3342
|
+
case "SLICE":
|
|
3343
|
+
throw new Error(`Sorry, Slices are not supported. Type:${node.type} id:${node.id}`);
|
|
3344
|
+
default:
|
|
3345
|
+
throw new Error(
|
|
3346
|
+
`Sorry, an unsupported node type was selected. Type:${node.type} id:${node.id}`
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3349
|
+
};
|
|
3350
|
+
var oldConvertNodesToAltNodes = (sceneNode, parent) => sceneNode.map(convertNodeToAltNode(parent)).filter(isNotEmpty);
|
|
3351
|
+
var cloneNode = (node, parent) => {
|
|
3352
|
+
const cloned = {};
|
|
3353
|
+
for (const prop in node) {
|
|
3354
|
+
if (prop !== "parent" && prop !== "children" && prop !== "horizontalPadding" && prop !== "verticalPadding" && prop !== "mainComponent" && prop !== "masterComponent" && prop !== "variantProperties" && prop !== "get_annotations" && prop !== "componentPropertyDefinitions" && prop !== "exposedInstances" && prop !== "instances" && prop !== "componentProperties" && prop !== "componenPropertyReferences" && prop !== "constrainProportions") {
|
|
3355
|
+
cloned[prop] = node[prop];
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
assignParent(parent, cloned);
|
|
3359
|
+
const altNode = __spreadProps(__spreadValues({}, cloned), {
|
|
3360
|
+
parent: cloned.parent,
|
|
3361
|
+
originalNode: node,
|
|
3362
|
+
canBeFlattened: canBeFlattened(node)
|
|
3363
|
+
});
|
|
3364
|
+
if (globalTextStyleSegments[node.id]) {
|
|
3365
|
+
;
|
|
3366
|
+
altNode.styledTextSegments = globalTextStyleSegments[node.id];
|
|
3367
|
+
}
|
|
3368
|
+
console.log("altnode:", altNode.parent, cloned.parent);
|
|
3369
|
+
return altNode;
|
|
3370
|
+
};
|
|
3371
|
+
var cloneAsRectangleNode = (node, parent) => {
|
|
3372
|
+
const clonedNode = cloneNode(node, parent);
|
|
3373
|
+
assignRectangleType(clonedNode);
|
|
3374
|
+
return clonedNode;
|
|
3375
|
+
};
|
|
3376
|
+
var extractStyledTextSegments = (node) => node.getStyledTextSegments([
|
|
3377
|
+
"fontName",
|
|
3378
|
+
"fills",
|
|
3379
|
+
"fontSize",
|
|
3380
|
+
"fontWeight",
|
|
3381
|
+
"hyperlink",
|
|
3382
|
+
"indentation",
|
|
3383
|
+
"letterSpacing",
|
|
3384
|
+
"lineHeight",
|
|
3385
|
+
"listOptions",
|
|
3386
|
+
"textCase",
|
|
3387
|
+
"textDecoration",
|
|
3388
|
+
"textStyleId",
|
|
3389
|
+
"fillStyleId",
|
|
3390
|
+
"openTypeFeatures"
|
|
3391
|
+
]);
|
|
3392
|
+
|
|
3393
|
+
// ../../packages/pluginMain/src/common/formatNodeName.ts
|
|
3394
|
+
function formatNodeName(name) {
|
|
3395
|
+
let lastName = name;
|
|
3396
|
+
lastName = lastName.replace(/\s+/g, "-");
|
|
3397
|
+
lastName = lastName.replace(/\//g, "-");
|
|
3398
|
+
return lastName;
|
|
3399
|
+
}
|
|
3400
|
+
|
|
3401
|
+
// ../../packages/pluginMain/src/code.ts
|
|
3402
|
+
var run = (settings) => __async(null, null, function* () {
|
|
3403
|
+
yield postLoadingAsync(true, { sleepAfter: 50 });
|
|
3404
|
+
resetPerformanceCounters();
|
|
3405
|
+
clearWarnings();
|
|
3406
|
+
const { useOldPluginVersion2025 } = settings;
|
|
3407
|
+
const selection = figma.currentPage.selection;
|
|
3408
|
+
if (selection.length === 0) {
|
|
3409
|
+
postEmptyMessage();
|
|
3410
|
+
return;
|
|
3411
|
+
}
|
|
3412
|
+
const nodeToJSONStart = Date.now();
|
|
3413
|
+
let convertedSelection;
|
|
3414
|
+
if (useOldPluginVersion2025) {
|
|
3415
|
+
convertedSelection = oldConvertNodesToAltNodes(selection, null);
|
|
3416
|
+
console.log("convertedSelection", convertedSelection);
|
|
3417
|
+
} else {
|
|
3418
|
+
convertedSelection = yield nodesToJSON(selection, settings);
|
|
3419
|
+
console.log(`[benchmark] nodesToJSON: ${Date.now() - nodeToJSONStart}ms`);
|
|
3420
|
+
console.log("nodeJson", convertedSelection);
|
|
3421
|
+
}
|
|
3422
|
+
console.log("[debug] convertedSelection", __spreadValues({}, convertedSelection[0]));
|
|
3423
|
+
if (convertedSelection.length === 0) {
|
|
3424
|
+
postEmptyMessage();
|
|
3425
|
+
return;
|
|
3426
|
+
}
|
|
3427
|
+
const selectedNode = selection[0];
|
|
3428
|
+
const selectedNodeImageBytes = yield selectedNode.exportAsync({
|
|
3429
|
+
format: "PNG",
|
|
3430
|
+
constraint: { type: "SCALE", value: 1 }
|
|
3431
|
+
});
|
|
3432
|
+
const convertToCodeStart = Date.now();
|
|
3433
|
+
const { html: code } = yield convertToCode(convertedSelection, settings);
|
|
3434
|
+
console.log(`[benchmark] convertToCode: ${Date.now() - convertToCodeStart}ms`);
|
|
3435
|
+
const colorPanelStart = Date.now();
|
|
3436
|
+
const colors = yield retrieveGenericSolidUIColors();
|
|
3437
|
+
const gradients = yield retrieveGenericLinearGradients();
|
|
3438
|
+
console.log(`[benchmark] color and gradient panel: ${Date.now() - colorPanelStart}ms`);
|
|
3439
|
+
console.log(`[benchmark] total generation time: ${Date.now() - nodeToJSONStart}ms`);
|
|
3440
|
+
console.log(
|
|
3441
|
+
`[benchmark] getNodeByIdAsync: ${getNodeByIdAsyncTime}ms (${getNodeByIdAsyncCalls} calls, avg: ${(getNodeByIdAsyncTime / getNodeByIdAsyncCalls || 1).toFixed(2)}ms)`
|
|
3442
|
+
);
|
|
3443
|
+
console.log(
|
|
3444
|
+
`[benchmark] getStyledTextSegments: ${getStyledTextSegmentsTime}ms (${getStyledTextSegmentsCalls} calls, avg: ${getStyledTextSegmentsCalls > 0 ? (getStyledTextSegmentsTime / getStyledTextSegmentsCalls).toFixed(2) : 0}ms)`
|
|
3445
|
+
);
|
|
3446
|
+
console.log(
|
|
3447
|
+
`[benchmark] processColorVariables: ${processColorVariablesTime}ms (${processColorVariablesCalls} calls, avg: ${processColorVariablesCalls > 0 ? (processColorVariablesTime / processColorVariablesCalls).toFixed(2) : 0}ms)`
|
|
3448
|
+
);
|
|
3449
|
+
postConversionComplete({
|
|
3450
|
+
selectedNodeImageBytes,
|
|
3451
|
+
selectedNodeName: `${formatNodeName(selectedNode.name)}-1x-${selectedNode.id}.png`,
|
|
3452
|
+
code,
|
|
3453
|
+
base64ImageList: [],
|
|
3454
|
+
colors,
|
|
3455
|
+
gradients,
|
|
3456
|
+
settings,
|
|
3457
|
+
warnings: [...warnings]
|
|
3458
|
+
});
|
|
3459
|
+
yield postLoadingAsync(false, { sleepBefore: 50 });
|
|
3460
|
+
});
|
|
3461
|
+
|
|
3462
|
+
// plugin-src/markUp.ts
|
|
3463
|
+
var currentImageScale = 2;
|
|
3464
|
+
var updateImageScale = (scale) => {
|
|
3465
|
+
currentImageScale = scale;
|
|
3466
|
+
console.log("[MarkUp] \u56FE\u7247\u5BFC\u51FA\u500D\u6570\u5DF2\u66F4\u65B0\u4E3A:", currentImageScale);
|
|
3467
|
+
};
|
|
3468
|
+
var initImageScale = () => __async(null, null, function* () {
|
|
3469
|
+
currentImageScale = yield getClientStorageWithDefault("image-scale", 2, PREFIX);
|
|
3470
|
+
console.log("[MarkUp] \u521D\u59CB\u5316\u56FE\u7247\u5BFC\u51FA\u500D\u6570:", currentImageScale);
|
|
3471
|
+
});
|
|
3472
|
+
var isProcessing = false;
|
|
3473
|
+
var runMarkUp = (extraType) => __async(null, null, function* () {
|
|
3474
|
+
const nodes = figma.currentPage.selection;
|
|
3475
|
+
if (nodes.length === 0) {
|
|
3476
|
+
figma.ui.postMessage({
|
|
3477
|
+
type: "markup-images",
|
|
3478
|
+
data: []
|
|
3479
|
+
});
|
|
3480
|
+
return;
|
|
3481
|
+
}
|
|
3482
|
+
if (isProcessing) {
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
isProcessing = true;
|
|
3486
|
+
yield postLoadingAsync(true, { sleepAfter: 50 });
|
|
3487
|
+
let imageDataList = [];
|
|
3488
|
+
if (extraType === "sliceBatch" /* SLICE_BATCH */) {
|
|
3489
|
+
const firstNode = nodes[0];
|
|
3490
|
+
if (firstNode && "children" in firstNode) {
|
|
3491
|
+
console.log(firstNode, firstNode.type, "nodes[0]");
|
|
3492
|
+
imageDataList = yield exportAllPngBytes(firstNode.children);
|
|
3493
|
+
}
|
|
3494
|
+
} else {
|
|
3495
|
+
imageDataList = yield exportAllPngBytes(nodes);
|
|
3496
|
+
}
|
|
3497
|
+
const transferableData = imageDataList.map((item) => {
|
|
3498
|
+
return {
|
|
3499
|
+
id: item.id,
|
|
3500
|
+
name: item.name,
|
|
3501
|
+
bytes: item.bytes,
|
|
3502
|
+
// Uint8Array 会自动作为 Transferable 传输
|
|
3503
|
+
width: item.width,
|
|
3504
|
+
height: item.height
|
|
3505
|
+
};
|
|
3506
|
+
});
|
|
3507
|
+
console.log("transferableData", transferableData);
|
|
3508
|
+
figma.ui.postMessage({
|
|
3509
|
+
type: "markup-images",
|
|
3510
|
+
data: transferableData,
|
|
3511
|
+
extraValue: extraType
|
|
3512
|
+
});
|
|
3513
|
+
console.log("postLoadingAsync", false);
|
|
3514
|
+
yield postLoadingAsync(false, { sleepBefore: 50 });
|
|
3515
|
+
isProcessing = false;
|
|
3516
|
+
});
|
|
3517
|
+
function exportAllPngBytes(nodes, options) {
|
|
3518
|
+
return __async(this, null, function* () {
|
|
3519
|
+
const _a = options || {
|
|
3520
|
+
format: "PNG"
|
|
3521
|
+
}, {
|
|
3522
|
+
format,
|
|
3523
|
+
constraint = { type: "SCALE", value: currentImageScale }
|
|
3524
|
+
} = _a, rest = __objRest(_a, [
|
|
3525
|
+
"format",
|
|
3526
|
+
"constraint"
|
|
3527
|
+
]);
|
|
3528
|
+
const imageDataList = [];
|
|
3529
|
+
const bytesPromises = [];
|
|
3530
|
+
for (const node of nodes) {
|
|
3531
|
+
console.log("nodeType", node.type);
|
|
3532
|
+
const bytesPromise = node.exportAsync(__spreadValues({
|
|
3533
|
+
format,
|
|
3534
|
+
constraint
|
|
3535
|
+
}, rest));
|
|
3536
|
+
bytesPromises.push(bytesPromise);
|
|
3537
|
+
}
|
|
3538
|
+
const nodeImageDataList = yield Promise.all(bytesPromises);
|
|
3539
|
+
nodeImageDataList.forEach((nodeImageData, index) => {
|
|
3540
|
+
const node = nodes[index];
|
|
3541
|
+
if (nodeImageData && node) {
|
|
3542
|
+
imageDataList.push({
|
|
3543
|
+
id: node.id,
|
|
3544
|
+
// 替换所有空格
|
|
3545
|
+
name: `${formatNodeName(node.name)}-${constraint.value}x-${node.id}.png`,
|
|
3546
|
+
bytes: nodeImageData,
|
|
3547
|
+
width: node.width,
|
|
3548
|
+
height: node.height,
|
|
3549
|
+
x: `${constraint.value}x`
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
});
|
|
3553
|
+
return imageDataList;
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
// plugin-src/code.ts
|
|
3558
|
+
var currentTab = "bind" /* BIND */;
|
|
3559
|
+
var currentMarkUpTabExtraType;
|
|
3560
|
+
var userPluginSettings;
|
|
3561
|
+
var defaultPluginSettings = {
|
|
3562
|
+
framework: "HTML",
|
|
3563
|
+
showLayerNames: false,
|
|
3564
|
+
useOldPluginVersion2025: false,
|
|
3565
|
+
responsiveRoot: false,
|
|
3566
|
+
useColorVariables: true,
|
|
3567
|
+
embedImages: true,
|
|
3568
|
+
embedVectors: true,
|
|
3569
|
+
htmlGenerationMode: "html"
|
|
3570
|
+
};
|
|
3571
|
+
var getUserSettings = () => __async(null, null, function* () {
|
|
3572
|
+
var _a;
|
|
3573
|
+
console.log("[DEBUG] getUserSettings - Starting to fetch user settings");
|
|
3574
|
+
const possiblePluginSrcSettings = (_a = yield figma.clientStorage.getAsync("userPluginSettings")) != null ? _a : {};
|
|
3575
|
+
console.log("[DEBUG] getUserSettings - Raw settings from storage:", possiblePluginSrcSettings);
|
|
3576
|
+
const updatedPluginSrcSettings = __spreadValues(__spreadValues({}, defaultPluginSettings), possiblePluginSrcSettings);
|
|
3577
|
+
userPluginSettings = __spreadProps(__spreadValues({}, updatedPluginSrcSettings), {
|
|
3578
|
+
framework: "HTML",
|
|
3579
|
+
htmlGenerationMode: "html"
|
|
3580
|
+
});
|
|
3581
|
+
console.log("[DEBUG] getUserSettings - Final settings:", userPluginSettings);
|
|
3582
|
+
return userPluginSettings;
|
|
3583
|
+
});
|
|
3584
|
+
var initSettings = () => __async(null, null, function* () {
|
|
3585
|
+
console.log("[DEBUG] initSettings - Initializing plugin settings");
|
|
3586
|
+
yield getUserSettings();
|
|
3587
|
+
postSettingsChanged(userPluginSettings);
|
|
3588
|
+
console.log("[DEBUG] initSettings - Calling safeRun with settings");
|
|
3589
|
+
safeRun(userPluginSettings);
|
|
3590
|
+
});
|
|
3591
|
+
var isLoading = false;
|
|
3592
|
+
var safeRun = (settings) => __async(null, null, function* () {
|
|
3593
|
+
console.log(
|
|
3594
|
+
"[DEBUG] safeRun - Called with isLoading =",
|
|
3595
|
+
isLoading,
|
|
3596
|
+
"selection =",
|
|
3597
|
+
figma.currentPage.selection
|
|
3598
|
+
);
|
|
3599
|
+
if (isLoading === false) {
|
|
3600
|
+
try {
|
|
3601
|
+
isLoading = true;
|
|
3602
|
+
console.log("[DEBUG] safeRun - Starting run execution");
|
|
3603
|
+
yield run(settings);
|
|
3604
|
+
console.log("[DEBUG] safeRun - Run execution completed");
|
|
3605
|
+
setTimeout(() => {
|
|
3606
|
+
console.log("[DEBUG] safeRun - Resetting isLoading to false");
|
|
3607
|
+
isLoading = false;
|
|
3608
|
+
}, 1);
|
|
3609
|
+
} catch (e) {
|
|
3610
|
+
console.log("[DEBUG] safeRun - Error caught in execution");
|
|
3611
|
+
isLoading = false;
|
|
3612
|
+
if (e && typeof e === "object" && "message" in e) {
|
|
3613
|
+
const error = e;
|
|
3614
|
+
console.log("error: ", error.stack);
|
|
3615
|
+
figma.ui.postMessage({ type: "error", error: error.message });
|
|
3616
|
+
} else {
|
|
3617
|
+
const errorMessage = String(e);
|
|
3618
|
+
console.log("Unknown error: ", errorMessage);
|
|
3619
|
+
figma.ui.postMessage({
|
|
3620
|
+
type: "error",
|
|
3621
|
+
error: errorMessage || "Unknown error occurred"
|
|
3622
|
+
});
|
|
3623
|
+
}
|
|
3624
|
+
figma.ui.postMessage({ type: "conversion-complete", success: false });
|
|
3625
|
+
}
|
|
3626
|
+
} else {
|
|
3627
|
+
console.log("[DEBUG] safeRun - Skipping execution because isLoading =", isLoading);
|
|
3628
|
+
}
|
|
3629
|
+
});
|
|
3630
|
+
var newRun = () => {
|
|
3631
|
+
switch (currentTab) {
|
|
3632
|
+
case "code" /* CODE */:
|
|
3633
|
+
safeRun(userPluginSettings);
|
|
3634
|
+
break;
|
|
3635
|
+
case "markUp" /* MARK_UP */:
|
|
3636
|
+
console.log("[DEBUG] runMarkUp", currentMarkUpTabExtraType);
|
|
3637
|
+
runMarkUp(currentMarkUpTabExtraType);
|
|
3638
|
+
break;
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
var standardMode = () => __async(null, null, function* () {
|
|
3642
|
+
console.log("[DEBUG] standardMode - Starting standard mode initialization");
|
|
3643
|
+
figma.showUI(__html__, { width: 450, height: 700, themeColors: true });
|
|
3644
|
+
yield initImageScale();
|
|
3645
|
+
newRun();
|
|
3646
|
+
figma.on("selectionchange", () => {
|
|
3647
|
+
console.log("[DEBUG] selectionchange event - New selection:", figma.currentPage.selection);
|
|
3648
|
+
newRun();
|
|
3649
|
+
});
|
|
3650
|
+
figma.loadAllPagesAsync();
|
|
3651
|
+
figma.on("documentchange", () => {
|
|
3652
|
+
});
|
|
3653
|
+
figma.ui.onmessage = (msg) => __async(null, null, function* () {
|
|
3654
|
+
console.log("[DEBUG] figma.ui.onmessage", msg);
|
|
3655
|
+
if (msg.type === "updateImageScale") {
|
|
3656
|
+
console.log("[DEBUG] updateImageScale", msg.scale);
|
|
3657
|
+
updateImageScale(msg.scale);
|
|
3658
|
+
} else if (msg.type === "regenerateSliceImages") {
|
|
3659
|
+
console.log("[DEBUG] regenerateSliceImages");
|
|
3660
|
+
runMarkUp(currentMarkUpTabExtraType);
|
|
3661
|
+
} else if (msg.type === "clientStorageChange") {
|
|
3662
|
+
console.log("[DEBUG] clientStorageChange", msg);
|
|
3663
|
+
yield handleClientStorageMessage({
|
|
3664
|
+
action: msg.action,
|
|
3665
|
+
key: msg.key,
|
|
3666
|
+
value: msg.value
|
|
3667
|
+
});
|
|
3668
|
+
if (msg.key === `${PREFIX}${LAYOUT_UNIT_STORAGE_KEY}` && currentTab === "code" /* CODE */) {
|
|
3669
|
+
safeRun(userPluginSettings);
|
|
3670
|
+
}
|
|
3671
|
+
} else if (msg.type === "clientStorageGet") {
|
|
3672
|
+
console.log("[DEBUG] clientStorageGet", msg);
|
|
3673
|
+
const value = yield figma.clientStorage.getAsync(msg.key);
|
|
3674
|
+
figma.ui.postMessage({
|
|
3675
|
+
type: "clientStorageResponse",
|
|
3676
|
+
requestId: msg.requestId,
|
|
3677
|
+
value: value !== void 0 ? value : null
|
|
3678
|
+
});
|
|
3679
|
+
} else if (msg.type === "tab-changed") {
|
|
3680
|
+
console.log("[DEBUG] currentTab", currentTab);
|
|
3681
|
+
const { value, extraValue } = msg;
|
|
3682
|
+
currentTab = value;
|
|
3683
|
+
currentMarkUpTabExtraType = extraValue;
|
|
3684
|
+
if (currentTab === "code" /* CODE */) {
|
|
3685
|
+
yield initSettings();
|
|
3686
|
+
} else {
|
|
3687
|
+
newRun();
|
|
3688
|
+
}
|
|
3689
|
+
} else if (msg.type === "pluginSettingWillChange") {
|
|
3690
|
+
const { key, value } = msg;
|
|
3691
|
+
if (key === "htmlGenerationMode") {
|
|
3692
|
+
return;
|
|
3693
|
+
}
|
|
3694
|
+
console.log(`[DEBUG] Setting changed: ${key} = ${String(value)}`);
|
|
3695
|
+
userPluginSettings[key] = value;
|
|
3696
|
+
figma.clientStorage.setAsync("userPluginSettings", userPluginSettings);
|
|
3697
|
+
safeRun(userPluginSettings);
|
|
3698
|
+
} else if (msg.type === "get-selection-json") {
|
|
3699
|
+
console.log("[DEBUG] get-selection-json message received");
|
|
3700
|
+
const nodes = figma.currentPage.selection;
|
|
3701
|
+
if (nodes.length === 0) {
|
|
3702
|
+
figma.ui.postMessage({
|
|
3703
|
+
type: "selection-json",
|
|
3704
|
+
data: { message: "No nodes selected" }
|
|
3705
|
+
});
|
|
3706
|
+
return;
|
|
3707
|
+
}
|
|
3708
|
+
const result = {};
|
|
3709
|
+
try {
|
|
3710
|
+
result.json = yield Promise.all(
|
|
3711
|
+
nodes.map(
|
|
3712
|
+
(node) => __async(null, null, function* () {
|
|
3713
|
+
return (yield node.exportAsync({
|
|
3714
|
+
format: "JSON_REST_V1"
|
|
3715
|
+
})).document;
|
|
3716
|
+
})
|
|
3717
|
+
)
|
|
3718
|
+
);
|
|
3719
|
+
} catch (error) {
|
|
3720
|
+
console.error("Error exporting JSON:", error);
|
|
3721
|
+
}
|
|
3722
|
+
try {
|
|
3723
|
+
const newNodes = yield nodesToJSON(nodes, userPluginSettings);
|
|
3724
|
+
const removeParent = (node) => {
|
|
3725
|
+
if (node.parent) {
|
|
3726
|
+
delete node.parent;
|
|
3727
|
+
}
|
|
3728
|
+
if (node.children) {
|
|
3729
|
+
node.children.forEach(removeParent);
|
|
3730
|
+
}
|
|
3731
|
+
};
|
|
3732
|
+
newNodes.forEach(removeParent);
|
|
3733
|
+
result.newConversion = newNodes;
|
|
3734
|
+
} catch (error) {
|
|
3735
|
+
console.error("Error in new conversion:", error);
|
|
3736
|
+
}
|
|
3737
|
+
figma.ui.postMessage({
|
|
3738
|
+
type: "selection-json",
|
|
3739
|
+
data: result
|
|
3740
|
+
});
|
|
3741
|
+
}
|
|
3742
|
+
});
|
|
3743
|
+
});
|
|
3744
|
+
var codegenMode = () => __async(null, null, function* () {
|
|
3745
|
+
console.log("[DEBUG] codegenMode - Starting codegen mode initialization");
|
|
3746
|
+
yield getUserSettings();
|
|
3747
|
+
figma.codegen.on(
|
|
3748
|
+
"generate",
|
|
3749
|
+
(_0) => __async(null, [_0], function* ({ language, node }) {
|
|
3750
|
+
console.log(`[DEBUG] codegen.generate - Language: ${language}, Node:`, node);
|
|
3751
|
+
if (language !== "html") {
|
|
3752
|
+
return [];
|
|
3753
|
+
}
|
|
3754
|
+
const convertedSelection = yield nodesToJSON([node], userPluginSettings);
|
|
3755
|
+
return [
|
|
3756
|
+
{
|
|
3757
|
+
title: "Code",
|
|
3758
|
+
code: (yield htmlMain(convertedSelection, userPluginSettings, true)).html,
|
|
3759
|
+
language: "HTML"
|
|
3760
|
+
},
|
|
3761
|
+
{
|
|
3762
|
+
title: "Text Styles",
|
|
3763
|
+
code: htmlCodeGenTextStyles(),
|
|
3764
|
+
language: "HTML"
|
|
3765
|
+
}
|
|
3766
|
+
];
|
|
3767
|
+
})
|
|
3768
|
+
);
|
|
3769
|
+
});
|
|
3770
|
+
switch (figma.mode) {
|
|
3771
|
+
case "default":
|
|
3772
|
+
case "inspect":
|
|
3773
|
+
console.log("[DEBUG] Starting plugin in", figma.mode, "mode");
|
|
3774
|
+
standardMode();
|
|
3775
|
+
break;
|
|
3776
|
+
case "codegen":
|
|
3777
|
+
console.log("[DEBUG] Starting plugin in codegen mode");
|
|
3778
|
+
codegenMode();
|
|
3779
|
+
break;
|
|
3780
|
+
default:
|
|
3781
|
+
console.log("[DEBUG] Unknown plugin mode:", figma.mode);
|
|
3782
|
+
break;
|
|
3783
|
+
}
|
|
3784
|
+
})();
|