├── src
├── ui.html
├── plugin.ts
└── ui.ts
├── .gitignore
├── assets
├── avatar.png
└── cover.png
├── tsconfig.json
├── manifest.json
├── package.json
├── .github
└── dependabot.yml
├── README.md
├── figplug.d.ts
└── figma.d.ts
/src/ui.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 | node_modules
3 | .DS_Store
--------------------------------------------------------------------------------
/assets/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brianlovin/figma-export-zip/HEAD/assets/avatar.png
--------------------------------------------------------------------------------
/assets/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brianlovin/figma-export-zip/HEAD/assets/cover.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "lib": [
5 | "es2017",
6 | "dom"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "api": "1.0.0",
3 | "name": "Export .zip",
4 | "main": "src/plugin.ts",
5 | "ui": "src/ui.ts",
6 | "id": "747228167548695118"
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "figma-export-zip",
3 | "version": "1.0.0",
4 | "description": "Easily export assets from Figma directly into a .zip file.",
5 | "dependencies": {
6 | "jszip": "^3.10.1"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "13:00"
8 | open-pull-requests-limit: 10
9 | assignees:
10 | - brianlovin
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Figma Export .zip 📦
4 |
5 | Easily export assets from Figma directly into a .zip file.
6 |
7 | [Install on Figma](https://www.figma.com/c/plugin/747228167548695118/Export-.zip)
8 |
9 | ## Usage
10 |
11 | 1. Select anything in Figma
12 | 2. Press `command + /` and search for 'Export .zip'
13 | 3. Run the plugin - your selection and all export settings will be used
14 | 4. `command + shift + p` to re-run the plugin instantly
15 |
16 | ## Installation and contributing
17 |
18 | 1. Clone the repository: `git clone https://github.com/brianlovin/figma-export-zip.git`
19 | 1. Go to the directory: `cd figma-export-zip`
20 | 1. Install `figplug`: `npm i -g figplug`
21 | 1. Install the packages: `npm install`
22 | 1. Build the plugin: `figplug build -w -o=build`
23 | 1. Add a new development plugin to Figma
24 | 1. Select the `figma-export-zip/build/manifest.json` file as the manifest
25 |
--------------------------------------------------------------------------------
/src/plugin.ts:
--------------------------------------------------------------------------------
1 | const { selection } = figma.currentPage
2 |
3 | function hasValidSelection(nodes) {
4 | return !(!nodes || nodes.length === 0)
5 | }
6 |
7 | export interface ExportableBytes {
8 | name: string;
9 | setting: ExportSettingsImage | ExportSettingsPDF | ExportSettingsSVG;
10 | bytes: Uint8Array;
11 | }
12 |
13 | async function main(nodes): Promise {
14 | if (!hasValidSelection(selection)) return Promise.resolve("Nothing selected for export")
15 |
16 | let exportableBytes: ExportableBytes[] = []
17 | for (let node of nodes) {
18 | let { name, exportSettings } = node
19 | if (exportSettings.length === 0) {
20 | exportSettings = [{ format: "PNG", suffix: '', constraint: { type: "SCALE", value: 1 }, contentsOnly: true }]
21 | }
22 |
23 | for (let setting of exportSettings) {
24 | let defaultSetting = setting
25 | const bytes = await node.exportAsync(defaultSetting)
26 | exportableBytes.push({
27 | name,
28 | setting,
29 | bytes,
30 | })
31 | }
32 | }
33 |
34 | figma.showUI(__html__, { visible: false })
35 | figma.ui.postMessage({ exportableBytes })
36 |
37 | return new Promise(res => {
38 | figma.ui.onmessage = () => res()
39 | })
40 | }
41 |
42 | main(selection).then(res => figma.closePlugin(res))
--------------------------------------------------------------------------------
/figplug.d.ts:
--------------------------------------------------------------------------------
1 | // Helpers provided automatically, as needed, by figplug.
2 |
3 | // symbolic type aliases
4 | type int = number
5 | type float = number
6 | type byte = number
7 | type bool = boolean
8 |
9 | // compile-time constants
10 | declare const DEBUG :boolean
11 | declare const VERSION :string
12 |
13 | // global namespace. Same as `window` in a regular web context.
14 | declare const global :{[k:string]:any}
15 |
16 | // panic prints a message, stack trace and exits the process
17 | //
18 | declare function panic(msg :any, ...v :any[]) :void
19 |
20 | // repr returns a detailed string representation of the input
21 | //
22 | declare function repr(obj :any) :string
23 |
24 | // print works just like console.log
25 | declare function print(msg :any, ...v :any[]) :void
26 |
27 | // dlog works just like console.log but is stripped out from non-debug builds
28 | declare function dlog(msg :any, ...v :any[]) :void
29 |
30 | // assert checks the condition for truth, and if false, prints an optional
31 | // message, stack trace and exits the process.
32 | // assert is removed in release builds
33 | declare var assert :AssertFun
34 | declare var AssertionError :ErrorConstructor
35 | declare interface AssertFun {
36 | (cond :any, msg? :string, cons? :Function) :void
37 |
38 | // throws can be set to true to cause assertions to be thrown as exceptions,
39 | // or set to false to cause the process to exit.
40 | // Only has an effect in Nodejs-like environments.
41 | // false by default.
42 | throws :bool
43 | }
44 |
--------------------------------------------------------------------------------
/src/ui.ts:
--------------------------------------------------------------------------------
1 | import JSZip from '../node_modules/jszip/dist/jszip.min.js';
2 |
3 | function typedArrayToBuffer(array) {
4 | return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
5 | }
6 |
7 | function exportTypeToBlobType(type: string) {
8 | switch(type) {
9 | case "PDF": return 'application/pdf'
10 | case "SVG": return 'image/svg+xml'
11 | case "PNG": return 'image/png'
12 | case "JPG": return 'image/jpeg'
13 | default: return 'image/png'
14 | }
15 | }
16 |
17 | function exportTypeToFileExtension(type: string) {
18 | switch(type) {
19 | case "PDF": return '.pdf'
20 | case "SVG": return '.svg'
21 | case "PNG": return '.png'
22 | case "JPG": return '.jpg'
23 | default: return '.png'
24 | }
25 | }
26 |
27 | window.onmessage = async (event) => {
28 | if (!event.data.pluginMessage) return
29 |
30 | const { exportableBytes } = event.data.pluginMessage
31 |
32 | return new Promise(resolve => {
33 | let zip = new JSZip();
34 |
35 | for (let data of exportableBytes) {
36 | const { bytes, name, setting } = data
37 | const cleanBytes = typedArrayToBuffer(bytes)
38 | const type = exportTypeToBlobType(setting.format)
39 | const extension = exportTypeToFileExtension(setting.format)
40 | let blob = new Blob([ cleanBytes ], { type })
41 | zip.file(`${name}${setting.suffix}${extension}`, blob, {base64: true});
42 | }
43 |
44 | zip.generateAsync({ type: 'blob' })
45 | .then((content: Blob) => {
46 | const blobURL = window.URL.createObjectURL(content);
47 | const link = document.createElement('a');
48 | link.className = 'button button--primary';
49 | link.href = blobURL;
50 | link.download = "export.zip"
51 | link.click()
52 | link.setAttribute('download', name + '.zip');
53 | resolve();
54 | });
55 | })
56 | .then(() => {
57 | window.parent.postMessage({ pluginMessage: 'Done!' }, '*')
58 | })
59 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------