├── 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 | ![gif](https://user-images.githubusercontent.com/35271042/62562846-bf601280-b836-11e9-9400-0f818c9d8fc5.gif) 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 | --------------------------------------------------------------------------------