├── tsconfig.json
├── manifest.json
├── .vscode
└── tasks.json
├── LICENSE
├── .gitignore
├── README.md
├── code.ts
├── code.js
└── figma.d.ts
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Chroma",
3 | "id": "739237058450529919",
4 | "api": "1.0.0",
5 | "main": "code.js",
6 | "editorType": ["figma"]
7 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "type": "typescript",
8 | "tsconfig": "tsconfig.json",
9 | "label": "TS - Watch Changes",
10 | "option": "watch",
11 | "problemMatcher": [
12 | "$tsc-watch"
13 | ],
14 | "runOptions": {
15 | "runOn": "folderOpen"
16 | },
17 | "group": {
18 | "kind": "build",
19 | "isDefault": true
20 | }
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Kaleidocode
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 | A Figma plugin for creating bulk color styles from selection.
8 |
9 |
10 |
11 |

12 |
13 |
14 |
15 | ## How to use
16 | Simply select any objects that have a fill color, run the plugin, and it will generate the color styles for you automatically.
17 |
18 | The color style name will be the same as your layer name. You can also add a "/" in your layer name to create color style groups.
19 |
20 | 
21 |
22 |
23 | ## Building from source
24 | This plugin template uses Typescript. If you are familiar with Javascript, Typescript will
25 | look very familiar. In fact, valid Javascript code is already valid Typescript code.
26 |
27 | Typescript adds type annotations to variables. This allows code editors such as Visual Studio Code
28 | to provide information about the Figma API while you are writing code, as well as help catch bugs
29 | you previously didn't notice.
30 |
31 | For more information, visit https://www.typescriptlang.org/
32 |
33 | Using Typescript requires a compiler to convert Typescript (code.ts) into Javascript (code.js)
34 | for the browser to run.
35 |
36 | To get the TypeScript compiler working:
37 |
38 | 1. Download Visual Studio Code if you haven't already: https://code.visualstudio.com/.
39 | 2. Install the TypeScript compiler globally: `sudo npm install -g typescript`.
40 | 3. Open this directory in Visual Studio Code.
41 | 4. Compile TypeScript to JavaScript: Run the "Terminal > Run Build Task..." menu item,
42 | then select "tsc: watch - tsconfig.json". You will have to do this again every time
43 | you reopen Visual Studio Code.
44 |
45 | That's it! Visual Studio Code will regenerate the JavaScript file every time you save.
46 |
--------------------------------------------------------------------------------
/code.ts:
--------------------------------------------------------------------------------
1 | if (figma.currentPage.selection.length <= 0){
2 | figma.closePlugin('Please select a Rectanle, Ellipse, or Polygon before running this plugin')
3 | }
4 |
5 | let addedCounter = 0
6 | let ref = []
7 | let selection = figma.currentPage.selection
8 |
9 | selection.forEach(c => {
10 | ref.push(c)
11 | })
12 |
13 | ref.sort(function (a, b) {
14 | return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' });
15 | });
16 | // TODO: turn this into an dropdown option
17 | // ref.reverse()
18 |
19 | ref.forEach((layer:any) => {
20 |
21 | // fills
22 | if (layer.fills.length > 0) {
23 | let colors = []
24 | let newStyle = figma.createPaintStyle()
25 |
26 | // only create description for single layer color fills
27 | if (layer.fills.length == 1) {
28 | // skip description for gradients
29 | if(layer.fills[0].type == 'GRADIENT_ANGULAR' || layer.fills[0].type == 'GRADIENT_DIAMOND' || layer.fills[0].type == 'GRADIENT_LINEAR' || layer.fills[0].type == 'GRADIENT_RADIAL'){
30 | console.log('skipping gradients')
31 | } else {
32 | newStyle.description = findTheHEX(layer.fills[0].color.r, layer.fills[0].color.g, layer.fills[0].color.b).toUpperCase()
33 | }
34 | }
35 |
36 | newStyle.name = layer.name
37 | layer.fills.forEach(item => {
38 | colors.push(item)
39 | });
40 | newStyle.paints = colors
41 | layer.fillStyleId = newStyle.id
42 |
43 | console.log(`+Added: ${newStyle.name}`)
44 |
45 | colors.length = 0
46 | addedCounter++
47 | }
48 |
49 | // strokes
50 | if (layer.strokes.length > 0) {
51 | console.log(layer)
52 | let strokes = []
53 | let newStyle = figma.createPaintStyle()
54 | newStyle.name = layer.name
55 | layer.strokes.forEach(item => {
56 | strokes.push(item)
57 | });
58 | newStyle.paints = strokes
59 | layer.strokeStyleId = newStyle.id
60 |
61 | strokes.length = 0
62 | addedCounter++
63 | }
64 | });
65 |
66 | figma.currentPage.selection = []
67 |
68 | figma.closePlugin(`🎉 ${addedCounter} styles added!`)
69 |
70 | function findTheHEX(red:any, green:any, blue:any) {
71 | var redHEX = rgbToHex(red)
72 | var greenHEX = rgbToHex(green)
73 | var blueHEX = rgbToHex(blue)
74 |
75 | return redHEX + greenHEX + blueHEX
76 | }
77 |
78 | function rgbToHex(rgb:any) {
79 | rgb = Math.floor(rgb * 255)
80 | var hex = Number(rgb).toString(16)
81 | if (hex.length < 2) {
82 | hex = '0' + hex
83 | }
84 | return hex
85 | }
--------------------------------------------------------------------------------
/code.js:
--------------------------------------------------------------------------------
1 | if (figma.currentPage.selection.length <= 0) {
2 | figma.closePlugin('Please select a Rectanle, Ellipse, or Polygon before running this plugin');
3 | }
4 | let addedCounter = 0;
5 | let ref = [];
6 | let selection = figma.currentPage.selection;
7 | selection.forEach(c => {
8 | ref.push(c);
9 | });
10 | ref.sort(function (a, b) {
11 | return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' });
12 | });
13 | // TODO: turn this into an dropdown option
14 | // ref.reverse()
15 | ref.forEach((layer) => {
16 | // fills
17 | if (layer.fills.length > 0) {
18 | let colors = [];
19 | let newStyle = figma.createPaintStyle();
20 | // only create description for single layer color fills
21 | if (layer.fills.length == 1) {
22 | // skip description for gradients
23 | if (layer.fills[0].type == 'GRADIENT_ANGULAR' || layer.fills[0].type == 'GRADIENT_DIAMOND' || layer.fills[0].type == 'GRADIENT_LINEAR' || layer.fills[0].type == 'GRADIENT_RADIAL') {
24 | console.log('skipping gradients');
25 | }
26 | else {
27 | newStyle.description = findTheHEX(layer.fills[0].color.r, layer.fills[0].color.g, layer.fills[0].color.b).toUpperCase();
28 | }
29 | }
30 | newStyle.name = layer.name;
31 | layer.fills.forEach(item => {
32 | colors.push(item);
33 | });
34 | newStyle.paints = colors;
35 | layer.fillStyleId = newStyle.id;
36 | console.log(`+Added: ${newStyle.name}`);
37 | colors.length = 0;
38 | addedCounter++;
39 | }
40 | // strokes
41 | if (layer.strokes.length > 0) {
42 | console.log(layer);
43 | let strokes = [];
44 | let newStyle = figma.createPaintStyle();
45 | newStyle.name = layer.name;
46 | layer.strokes.forEach(item => {
47 | strokes.push(item);
48 | });
49 | newStyle.paints = strokes;
50 | layer.strokeStyleId = newStyle.id;
51 | strokes.length = 0;
52 | addedCounter++;
53 | }
54 | });
55 | figma.currentPage.selection = [];
56 | figma.closePlugin(`🎉 ${addedCounter} styles added!`);
57 | function findTheHEX(red, green, blue) {
58 | var redHEX = rgbToHex(red);
59 | var greenHEX = rgbToHex(green);
60 | var blueHEX = rgbToHex(blue);
61 | return redHEX + greenHEX + blueHEX;
62 | }
63 | function rgbToHex(rgb) {
64 | rgb = Math.floor(rgb * 255);
65 | var hex = Number(rgb).toString(16);
66 | if (hex.length < 2) {
67 | hex = '0' + hex;
68 | }
69 | return hex;
70 | }
71 |
--------------------------------------------------------------------------------
/figma.d.ts:
--------------------------------------------------------------------------------
1 | // Global variable with Figma's plugin API.
2 | declare const figma: PluginAPI
3 | declare const __html__: string
4 |
5 | interface PluginAPI {
6 | readonly apiVersion: "1.0.0"
7 | readonly command: string
8 | readonly root: DocumentNode
9 | readonly viewport: ViewportAPI
10 | closePlugin(message?: string): void
11 |
12 | showUI(html: string, options?: ShowUIOptions): void
13 | readonly ui: UIAPI
14 |
15 | readonly clientStorage: ClientStorageAPI
16 |
17 | getNodeById(id: string): BaseNode | null
18 | getStyleById(id: string): BaseStyle | null
19 |
20 | currentPage: PageNode
21 |
22 | readonly mixed: symbol
23 |
24 | createRectangle(): RectangleNode
25 | createLine(): LineNode
26 | createEllipse(): EllipseNode
27 | createPolygon(): PolygonNode
28 | createStar(): StarNode
29 | createVector(): VectorNode
30 | createText(): TextNode
31 | createBooleanOperation(): BooleanOperationNode
32 | createFrame(): FrameNode
33 | createComponent(): ComponentNode
34 | createPage(): PageNode
35 | createSlice(): SliceNode
36 |
37 | createPaintStyle(): PaintStyle
38 | createTextStyle(): TextStyle
39 | createEffectStyle(): EffectStyle
40 | createGridStyle(): GridStyle
41 |
42 | importComponentByKeyAsync(key: string): Promise
43 | importStyleByKeyAsync(key: string): Promise
44 |
45 | listAvailableFontsAsync(): Promise
46 | loadFontAsync(fontName: FontName): Promise
47 | readonly hasMissingFont: boolean
48 |
49 | createNodeFromSvg(svg: string): FrameNode
50 |
51 | createImage(data: Uint8Array): Image
52 | getImageByHash(hash: string): Image
53 |
54 | group(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): FrameNode
55 | flatten(nodes: ReadonlyArray, parent?: BaseNode & ChildrenMixin, index?: number): VectorNode
56 | }
57 |
58 | interface ClientStorageAPI {
59 | getAsync(key: string): Promise
60 | setAsync(key: string, value: any): Promise
61 | }
62 |
63 | type ShowUIOptions = {
64 | visible?: boolean,
65 | width?: number,
66 | height?: number,
67 | }
68 |
69 | type UIPostMessageOptions = {
70 | targetOrigin?: string,
71 | }
72 |
73 | type OnMessageProperties = {
74 | sourceOrigin: string,
75 | }
76 |
77 | interface UIAPI {
78 | show(): void
79 | hide(): void
80 | resize(width: number, height: number): void
81 | close(): void
82 |
83 | postMessage(pluginMessage: any, options?: UIPostMessageOptions): void
84 | onmessage: ((pluginMessage: any, props: OnMessageProperties) => void) | undefined
85 | }
86 |
87 | interface ViewportAPI {
88 | center: { x: number, y: number }
89 | zoom: number
90 | scrollAndZoomIntoView(nodes: ReadonlyArray)
91 | }
92 |
93 | ////////////////////////////////////////////////////////////////////////////////
94 | // Datatypes
95 |
96 | type Transform = [
97 | [number, number, number],
98 | [number, number, number]
99 | ]
100 |
101 | interface Vector {
102 | readonly x: number
103 | readonly y: number
104 | }
105 |
106 | interface RGB {
107 | readonly r: number
108 | readonly g: number
109 | readonly b: number
110 | }
111 |
112 | interface RGBA {
113 | readonly r: number
114 | readonly g: number
115 | readonly b: number
116 | readonly a: number
117 | }
118 |
119 | interface FontName {
120 | readonly family: string
121 | readonly style: string
122 | }
123 |
124 | type TextCase = "ORIGINAL" | "UPPER" | "LOWER" | "TITLE"
125 |
126 | type TextDecoration = "NONE" | "UNDERLINE" | "STRIKETHROUGH"
127 |
128 | interface ArcData {
129 | readonly startingAngle: number
130 | readonly endingAngle: number
131 | readonly innerRadius: number
132 | }
133 |
134 | interface ShadowEffect {
135 | readonly type: "DROP_SHADOW" | "INNER_SHADOW"
136 | readonly color: RGBA
137 | readonly offset: Vector
138 | readonly radius: number
139 | readonly visible: boolean
140 | readonly blendMode: BlendMode
141 | }
142 |
143 | interface BlurEffect {
144 | readonly type: "LAYER_BLUR" | "BACKGROUND_BLUR"
145 | readonly radius: number
146 | readonly visible: boolean
147 | }
148 |
149 | type Effect = ShadowEffect | BlurEffect
150 |
151 | type ConstraintType = "MIN" | "CENTER" | "MAX" | "STRETCH" | "SCALE"
152 |
153 | interface Constraints {
154 | readonly horizontal: ConstraintType
155 | readonly vertical: ConstraintType
156 | }
157 |
158 | interface ColorStop {
159 | readonly position: number
160 | readonly color: RGBA
161 | }
162 |
163 | interface ImageFilters {
164 | exposure?: number
165 | contrast?: number
166 | saturation?: number
167 | temperature?: number
168 | tint?: number
169 | highlights?: number
170 | shadows?: number
171 | }
172 |
173 | interface SolidPaint {
174 | readonly type: "SOLID"
175 | readonly color: RGB
176 |
177 | readonly visible?: boolean
178 | readonly opacity?: number
179 | readonly blendMode?: BlendMode
180 | }
181 |
182 | interface GradientPaint {
183 | readonly type: "GRADIENT_LINEAR" | "GRADIENT_RADIAL" | "GRADIENT_ANGULAR" | "GRADIENT_DIAMOND"
184 | readonly gradientTransform: Transform
185 | readonly gradientStops: ReadonlyArray
186 |
187 | readonly visible?: boolean
188 | readonly opacity?: number
189 | readonly blendMode?: BlendMode
190 | }
191 |
192 | interface ImagePaint {
193 | readonly type: "IMAGE"
194 | readonly scaleMode: "FILL" | "FIT" | "CROP" | "TILE"
195 | readonly imageHash: string | null
196 | readonly imageTransform?: Transform // setting for "CROP"
197 | readonly scalingFactor?: number // setting for "TILE"
198 | readonly filters?: ImageFilters
199 |
200 | readonly visible?: boolean
201 | readonly opacity?: number
202 | readonly blendMode?: BlendMode
203 | }
204 |
205 | type Paint = SolidPaint | GradientPaint | ImagePaint
206 |
207 | interface Guide {
208 | readonly axis: "X" | "Y"
209 | readonly offset: number
210 | }
211 |
212 | interface RowsColsLayoutGrid {
213 | readonly pattern: "ROWS" | "COLUMNS"
214 | readonly alignment: "MIN" | "MAX" | "STRETCH" | "CENTER"
215 | readonly gutterSize: number
216 |
217 | readonly count: number // Infinity when "Auto" is set in the UI
218 | readonly sectionSize?: number // Not set for alignment: "STRETCH"
219 | readonly offset?: number // Not set for alignment: "CENTER"
220 |
221 | readonly visible?: boolean
222 | readonly color?: RGBA
223 | }
224 |
225 | interface GridLayoutGrid {
226 | readonly pattern: "GRID"
227 | readonly sectionSize: number
228 |
229 | readonly visible?: boolean
230 | readonly color?: RGBA
231 | }
232 |
233 | type LayoutGrid = RowsColsLayoutGrid | GridLayoutGrid
234 |
235 | interface ExportSettingsConstraints {
236 | type: "SCALE" | "WIDTH" | "HEIGHT"
237 | value: number
238 | }
239 |
240 | interface ExportSettingsImage {
241 | format: "JPG" | "PNG"
242 | contentsOnly?: boolean // defaults to true
243 | suffix?: string
244 | constraint?: ExportSettingsConstraints
245 | }
246 |
247 | interface ExportSettingsSVG {
248 | format: "SVG"
249 | contentsOnly?: boolean // defaults to true
250 | suffix?: string
251 | svgOutlineText?: boolean // defaults to true
252 | svgIdAttribute?: boolean // defaults to false
253 | svgSimplifyStroke?: boolean // defaults to true
254 | }
255 |
256 | interface ExportSettingsPDF {
257 | format: "PDF"
258 | contentsOnly?: boolean // defaults to true
259 | suffix?: string
260 | }
261 |
262 | type ExportSettings = ExportSettingsImage | ExportSettingsSVG | ExportSettingsPDF
263 |
264 | type WindingRule = "NONZERO" | "EVENODD"
265 |
266 | interface VectorVertex {
267 | readonly x: number
268 | readonly y: number
269 | readonly strokeCap?: StrokeCap
270 | readonly strokeJoin?: StrokeJoin
271 | readonly cornerRadius?: number
272 | readonly handleMirroring?: HandleMirroring
273 | }
274 |
275 | interface VectorSegment {
276 | readonly start: number
277 | readonly end: number
278 | readonly tangentStart?: Vector // Defaults to { x: 0, y: 0 }
279 | readonly tangentEnd?: Vector // Defaults to { x: 0, y: 0 }
280 | }
281 |
282 | interface VectorRegion {
283 | readonly windingRule: WindingRule
284 | readonly loops: ReadonlyArray>
285 | }
286 |
287 | interface VectorNetwork {
288 | readonly vertices: ReadonlyArray
289 | readonly segments: ReadonlyArray
290 | readonly regions?: ReadonlyArray // Defaults to []
291 | }
292 |
293 | interface VectorPath {
294 | readonly windingRule: WindingRule | "NONE"
295 | readonly data: string
296 | }
297 |
298 | type VectorPaths = ReadonlyArray
299 |
300 | type LetterSpacing = {
301 | readonly value: number
302 | readonly unit: "PIXELS" | "PERCENT"
303 | }
304 |
305 | type LineHeight = {
306 | readonly value: number
307 | readonly unit: "PIXELS" | "PERCENT"
308 | } | {
309 | readonly unit: "AUTO"
310 | }
311 |
312 | type BlendMode =
313 | "PASS_THROUGH" |
314 | "NORMAL" |
315 | "DARKEN" |
316 | "MULTIPLY" |
317 | "LINEAR_BURN" |
318 | "COLOR_BURN" |
319 | "LIGHTEN" |
320 | "SCREEN" |
321 | "LINEAR_DODGE" |
322 | "COLOR_DODGE" |
323 | "OVERLAY" |
324 | "SOFT_LIGHT" |
325 | "HARD_LIGHT" |
326 | "DIFFERENCE" |
327 | "EXCLUSION" |
328 | "HUE" |
329 | "SATURATION" |
330 | "COLOR" |
331 | "LUMINOSITY"
332 |
333 | interface Font {
334 | fontName: FontName
335 | }
336 |
337 | ////////////////////////////////////////////////////////////////////////////////
338 | // Mixins
339 |
340 | interface BaseNodeMixin {
341 | readonly id: string
342 | readonly parent: (BaseNode & ChildrenMixin) | null
343 | name: string // Note: setting this also sets `autoRename` to false on TextNodes
344 | readonly removed: boolean
345 | toString(): string
346 | remove(): void
347 |
348 | getPluginData(key: string): string
349 | setPluginData(key: string, value: string): void
350 |
351 | // Namespace is a string that must be at least 3 alphanumeric characters, and should
352 | // be a name related to your plugin. Other plugins will be able to read this data.
353 | getSharedPluginData(namespace: string, key: string): string
354 | setSharedPluginData(namespace: string, key: string, value: string): void
355 | }
356 |
357 | interface SceneNodeMixin {
358 | visible: boolean
359 | locked: boolean
360 | }
361 |
362 | interface ChildrenMixin {
363 | readonly children: ReadonlyArray
364 |
365 | appendChild(child: BaseNode): void
366 | insertChild(index: number, child: BaseNode): void
367 |
368 | findAll(callback?: (node: BaseNode) => boolean): ReadonlyArray
369 | findOne(callback: (node: BaseNode) => boolean): BaseNode | null
370 | }
371 |
372 | interface ConstraintMixin {
373 | constraints: Constraints
374 | }
375 |
376 | interface LayoutMixin {
377 | readonly absoluteTransform: Transform
378 | relativeTransform: Transform
379 | x: number
380 | y: number
381 | rotation: number // In degrees
382 |
383 | readonly width: number
384 | readonly height: number
385 |
386 | resize(width: number, height: number): void
387 | resizeWithoutConstraints(width: number, height: number): void
388 | }
389 |
390 | interface BlendMixin {
391 | opacity: number
392 | blendMode: BlendMode
393 | isMask: boolean
394 | effects: ReadonlyArray
395 | effectStyleId: string
396 | }
397 |
398 | interface FrameMixin {
399 | backgrounds: ReadonlyArray
400 | layoutGrids: ReadonlyArray
401 | clipsContent: boolean
402 | guides: ReadonlyArray
403 | gridStyleId: string
404 | backgroundStyleId: string
405 | }
406 |
407 | type StrokeCap = "NONE" | "ROUND" | "SQUARE" | "ARROW_LINES" | "ARROW_EQUILATERAL"
408 | type StrokeJoin = "MITER" | "BEVEL" | "ROUND"
409 | type HandleMirroring = "NONE" | "ANGLE" | "ANGLE_AND_LENGTH"
410 |
411 | interface GeometryMixin {
412 | fills: ReadonlyArray | symbol
413 | strokes: ReadonlyArray
414 | strokeWeight: number
415 | strokeAlign: "CENTER" | "INSIDE" | "OUTSIDE"
416 | strokeCap: StrokeCap | symbol
417 | strokeJoin: StrokeJoin | symbol
418 | dashPattern: ReadonlyArray
419 | fillStyleId: string | symbol
420 | strokeStyleId: string
421 | }
422 |
423 | interface CornerMixin {
424 | cornerRadius: number | symbol
425 | cornerSmoothing: number
426 | }
427 |
428 | interface ExportMixin {
429 | exportSettings: ExportSettings[]
430 | exportAsync(settings?: ExportSettings): Promise // Defaults to PNG format
431 | }
432 |
433 | interface DefaultShapeMixin extends
434 | BaseNodeMixin, SceneNodeMixin,
435 | BlendMixin, GeometryMixin, LayoutMixin, ExportMixin {
436 | }
437 |
438 | interface DefaultContainerMixin extends
439 | BaseNodeMixin, SceneNodeMixin,
440 | ChildrenMixin, FrameMixin,
441 | BlendMixin, ConstraintMixin, LayoutMixin, ExportMixin {
442 | }
443 |
444 | ////////////////////////////////////////////////////////////////////////////////
445 | // Nodes
446 |
447 | interface DocumentNode extends BaseNodeMixin, ChildrenMixin {
448 | readonly type: "DOCUMENT"
449 | }
450 |
451 | interface PageNode extends BaseNodeMixin, ChildrenMixin, ExportMixin {
452 | readonly type: "PAGE"
453 | clone(): PageNode
454 |
455 | guides: ReadonlyArray
456 | selection: ReadonlyArray
457 | }
458 |
459 | interface FrameNode extends DefaultContainerMixin {
460 | readonly type: "FRAME" | "GROUP"
461 | clone(): FrameNode
462 | }
463 |
464 | interface SliceNode extends BaseNodeMixin, SceneNodeMixin, LayoutMixin, ExportMixin {
465 | readonly type: "SLICE"
466 | clone(): SliceNode
467 | }
468 |
469 | interface RectangleNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin {
470 | readonly type: "RECTANGLE"
471 | clone(): RectangleNode
472 | topLeftRadius: number
473 | topRightRadius: number
474 | bottomLeftRadius: number
475 | bottomRightRadius: number
476 | }
477 |
478 | interface LineNode extends DefaultShapeMixin, ConstraintMixin {
479 | readonly type: "LINE"
480 | clone(): LineNode
481 | }
482 |
483 | interface EllipseNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin {
484 | readonly type: "ELLIPSE"
485 | clone(): EllipseNode
486 | arcData: ArcData
487 | }
488 |
489 | interface PolygonNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin {
490 | readonly type: "POLYGON"
491 | clone(): PolygonNode
492 | pointCount: number
493 | }
494 |
495 | interface StarNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin {
496 | readonly type: "STAR"
497 | clone(): StarNode
498 | pointCount: number
499 | innerRadius: number
500 | }
501 |
502 | interface VectorNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin {
503 | readonly type: "VECTOR"
504 | clone(): VectorNode
505 | vectorNetwork: VectorNetwork
506 | vectorPaths: VectorPaths
507 | handleMirroring: HandleMirroring | symbol
508 | }
509 |
510 | interface TextNode extends DefaultShapeMixin, ConstraintMixin {
511 | readonly type: "TEXT"
512 | clone(): TextNode
513 | characters: string
514 | readonly hasMissingFont: boolean
515 | textAlignHorizontal: "LEFT" | "CENTER" | "RIGHT" | "JUSTIFIED"
516 | textAlignVertical: "TOP" | "CENTER" | "BOTTOM"
517 | textAutoResize: "NONE" | "WIDTH_AND_HEIGHT" | "HEIGHT"
518 | paragraphIndent: number
519 | paragraphSpacing: number
520 | autoRename: boolean
521 |
522 | textStyleId: string | symbol
523 | fontSize: number | symbol
524 | fontName: FontName | symbol
525 | textCase: TextCase | symbol
526 | textDecoration: TextDecoration | symbol
527 | letterSpacing: LetterSpacing | symbol
528 | lineHeight: LineHeight | symbol
529 |
530 | getRangeFontSize(start: number, end: number): number | symbol
531 | setRangeFontSize(start: number, end: number, value: number): void
532 | getRangeFontName(start: number, end: number): FontName | symbol
533 | setRangeFontName(start: number, end: number, value: FontName): void
534 | getRangeTextCase(start: number, end: number): TextCase | symbol
535 | setRangeTextCase(start: number, end: number, value: TextCase): void
536 | getRangeTextDecoration(start: number, end: number): TextDecoration | symbol
537 | setRangeTextDecoration(start: number, end: number, value: TextDecoration): void
538 | getRangeLetterSpacing(start: number, end: number): LetterSpacing | symbol
539 | setRangeLetterSpacing(start: number, end: number, value: LetterSpacing): void
540 | getRangeLineHeight(start: number, end: number): LineHeight | symbol
541 | setRangeLineHeight(start: number, end: number, value: LineHeight): void
542 | getRangeFills(start: number, end: number): Paint[] | symbol
543 | setRangeFills(start: number, end: number, value: Paint[]): void
544 | getRangeTextStyleId(start: number, end: number): string | symbol
545 | setRangeTextStyleId(start: number, end: number, value: string): void
546 | getRangeFillStyleId(start: number, end: number): string | symbol
547 | setRangeFillStyleId(start: number, end: number, value: string): void
548 | }
549 |
550 | interface ComponentNode extends DefaultContainerMixin {
551 | readonly type: "COMPONENT"
552 | clone(): ComponentNode
553 |
554 | createInstance(): InstanceNode
555 | description: string
556 | readonly remote: boolean
557 | readonly key: string // The key to use with "importComponentByKeyAsync"
558 | }
559 |
560 | interface InstanceNode extends DefaultContainerMixin {
561 | readonly type: "INSTANCE"
562 | clone(): InstanceNode
563 | masterComponent: ComponentNode
564 | }
565 |
566 | interface BooleanOperationNode extends DefaultShapeMixin, ChildrenMixin, CornerMixin {
567 | readonly type: "BOOLEAN_OPERATION"
568 | clone(): BooleanOperationNode
569 | booleanOperation: "UNION" | "INTERSECT" | "SUBTRACT" | "EXCLUDE"
570 | }
571 |
572 | type BaseNode =
573 | DocumentNode |
574 | PageNode |
575 | SceneNode
576 |
577 | type SceneNode =
578 | SliceNode |
579 | FrameNode |
580 | ComponentNode |
581 | InstanceNode |
582 | BooleanOperationNode |
583 | VectorNode |
584 | StarNode |
585 | LineNode |
586 | EllipseNode |
587 | PolygonNode |
588 | RectangleNode |
589 | TextNode
590 |
591 | type NodeType =
592 | "DOCUMENT" |
593 | "PAGE" |
594 | "SLICE" |
595 | "FRAME" |
596 | "GROUP" |
597 | "COMPONENT" |
598 | "INSTANCE" |
599 | "BOOLEAN_OPERATION" |
600 | "VECTOR" |
601 | "STAR" |
602 | "LINE" |
603 | "ELLIPSE" |
604 | "POLYGON" |
605 | "RECTANGLE" |
606 | "TEXT"
607 |
608 | ////////////////////////////////////////////////////////////////////////////////
609 | // Styles
610 | type StyleType = "PAINT" | "TEXT" | "EFFECT" | "GRID"
611 |
612 | interface BaseStyle {
613 | readonly id: string
614 | readonly type: StyleType
615 | name: string
616 | description: string
617 | remote: boolean
618 | readonly key: string // The key to use with "importStyleByKeyAsync"
619 | remove(): void
620 | }
621 |
622 | interface PaintStyle extends BaseStyle {
623 | type: "PAINT"
624 | paints: ReadonlyArray
625 | }
626 |
627 | interface TextStyle extends BaseStyle {
628 | type: "TEXT"
629 | fontSize: number
630 | textDecoration: TextDecoration
631 | fontName: FontName
632 | letterSpacing: LetterSpacing
633 | lineHeight: LineHeight
634 | paragraphIndent: number
635 | paragraphSpacing: number
636 | textCase: TextCase
637 | }
638 |
639 | interface EffectStyle extends BaseStyle {
640 | type: "EFFECT"
641 | effects: ReadonlyArray
642 | }
643 |
644 | interface GridStyle extends BaseStyle {
645 | type: "GRID"
646 | layoutGrids: ReadonlyArray
647 | }
648 |
649 | ////////////////////////////////////////////////////////////////////////////////
650 | // Other
651 |
652 | interface Image {
653 | readonly hash: string
654 | getBytesAsync(): Promise
655 | }
656 |
--------------------------------------------------------------------------------