-
Notifications
You must be signed in to change notification settings - Fork 2
/
code.ts
191 lines (172 loc) · 6.29 KB
/
code.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* eslint-disable @typescript-eslint/no-explicit-any */
// This file holds the main code for plugins. Code in this file has access to
// the *figma document* via the figma global object.
// Send the extracted layers to the UI
figma.showUI(__html__, {
themeColors: true,
width: 400,
height: 400
});
// Initialize an empty array to hold the extracted layers
const layers: any[] = [];
// Helper function to extract common properties (like fills, strokes, and effects)
function extractCommonProperties(node: SceneNode) {
return {
fills: 'fills' in node ? node.fills : null,
strokes: 'strokes' in node ? node.strokes : null,
effects: 'effects' in node ? node.effects : null,
};
}
// Helper function to extract the numeric part from node.id (after the colon)
function extractId(id: string): number {
const parts = id.split(':');
return parts.length > 1 ? parseInt(parts[1], 10) : parseInt(id, 10);
}
// Helper function to generate a UUID
function generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0,
v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
// Function to extract all layers, recursively traversing child layers
async function extractLayers(node: SceneNode) {
// Create a new layer object with common properties
const layer: any = {
id: generateUUID(),
type: node.type,
name: node.name,
x: node.x,
y: node.y,
width: node.width,
height: node.height,
zIndex: extractId(node.id),
...extractCommonProperties(node) // Add common properties
};
// If it's a text node, extract text-specific properties
if (node.type === 'TEXT') {
const textNode = node as TextNode;
layer.characters = textNode.characters;
layer.fontSize = textNode.fontSize;
layer.fontName = textNode.fontName;
layer.fontWeight = textNode.fontWeight;
layer.textAlignHorizontal = textNode.textAlignHorizontal;
layer.textAlignVertical = textNode.textAlignVertical;
layer.letterSpacing = textNode.letterSpacing;
layer.lineHeight = textNode.lineHeight;
layer.paragraphIndent = textNode.paragraphIndent;
layer.paragraphSpacing = textNode.paragraphSpacing;
layer.textCase = textNode.textCase;
layer.textDecoration = textNode.textDecoration;
layer.textAutoResize = textNode.textAutoResize;
layer.rotation = Math.abs(textNode.rotation > 180 ? textNode.rotation - 360 : textNode.rotation);
}
// If it's a ellipse node, extract ellipse-specific properties
if (node.type === 'ELLIPSE') {
const ellipseNode = node as EllipseNode;
layer.arcData = ellipseNode.arcData;
layer.isMask = ellipseNode.isMask;
layer.markType = ellipseNode.maskType;
layer.cornerRadius = ellipseNode.cornerRadius;
}
// If it's a rectangle node, extract rectangle-specific properties
if (node.type === 'RECTANGLE') {
const rectangleNode = node as RectangleNode;
layer.isMask = rectangleNode.isMask;
layer.markType = rectangleNode.maskType;
if (rectangleNode.cornerRadius !== figma.mixed) {
layer.cornerRadius = rectangleNode.cornerRadius;
} else {
layer.cornerRadius = {
topLeft: rectangleNode.topLeftRadius,
topRight: rectangleNode.topRightRadius,
bottomLeft: rectangleNode.bottomLeftRadius,
bottomRight: rectangleNode.bottomRightRadius
};
}
if (rectangleNode.strokeWeight !== figma.mixed) {
layer.strokeWeight = rectangleNode.strokeWeight;
} else {
layer.strokeWeight = {
top: rectangleNode.strokeTopWeight,
right: rectangleNode.strokeRightWeight,
bottom: rectangleNode.strokeBottomWeight,
left: rectangleNode.strokeLeftWeight
};
}
layer.relativeTransform = rectangleNode.relativeTransform;
layer.absoluteTransform = rectangleNode.absoluteTransform;
layer.rotation = rectangleNode.rotation;
}
// If it's a vector node, extract vector-specific properties
if (node.type === 'VECTOR') {
const vectorNode = node as VectorNode;
layer.vectorPaths = vectorNode.vectorPaths;
layer.isMask = vectorNode.isMask;
layer.markType = vectorNode.maskType;
}
// Check if it's an image or a node containing image data
if (node.type === 'RECTANGLE' || node.type === 'ELLIPSE' || node.type === 'FRAME' || node.type === 'BOOLEAN_OPERATION' || node.type === 'VECTOR') {
const fills = (node as GeometryMixin).fills as Paint[];
for (const paint of fills) {
if (paint.type === 'IMAGE') {
const imageHash = paint.imageHash;
if (imageHash) {
const imageByHash = figma.getImageByHash(imageHash);
if (imageByHash) {
const imageBytes = await imageByHash.getBytesAsync();
if (imageBytes) {
const image = {
id: layer.id,
bytes: imageBytes
}
// Send the image data to the UI
figma.ui.postMessage({ type: 'image', data: image });
} else {
figma.notify('Error: Could not extract image)', { error: true });
}
}
}
}
}
}
// Recursively extract children if applicable (for frames, groups, etc.)
// If it's a frame, group, or component, recursively extract its children
if ('children' in node) {
const childLayers: any[] = [];
for (const child of node.children) {
const childLayer = await extractLayers(child);
childLayers.push(childLayer);
}
layer.layers = childLayers;
}
return layer;
}
// Traverse the selection and extract all layers using async/await
async function extractSelectedLayers() {
const selection = figma.currentPage.selection;
if (selection.length === 0) {
figma.notify('Please select one or more layers to extract.', { error: true });
return;
}
for (const node of selection) {
const extractedLayer = await extractLayers(node);
layers.push(extractedLayer);
}
// Send the extracted layers to the UI
figma.ui.postMessage({ type: 'layers', data: JSON.stringify(layers) });
}
// Handle messages from UI
figma.ui.onmessage = async (msg) => {
if (msg.type === 'close') {
figma.closePlugin();
}
if (msg.type === 'generate') {
try {
await extractSelectedLayers();
} catch (error) {
figma.notify(`Error: ${error}`, { error: true });
}
}
};