├── LICENSE.md ├── README.md ├── code.js ├── code.ts ├── data_from_local.png ├── figma.d.ts ├── manifest.json ├── tsconfig.json └── ui.html /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Ashung Hung (ashung.hung@gmail.com) 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](data_from_local.png) 2 | 3 | # Data From Local 4 | 5 | Figma plugin to insert data from local TXT file or JPG / PNG images. 6 | 7 | ##### Text Data 8 | 9 | Text data must saved as a .TXT file with each data value on a new line. 10 | 11 | ##### Image Data 12 | 13 | Only support JPG and PNG files, you can insert image to any shape layer. 14 | 15 | ## Installation 16 | 17 | https://www.figma.com/c/plugin/759249283654441170 18 | 19 | ## License 20 | 21 | MIT 22 | 23 | ## Donate 24 | 25 | [Buy me a coffee](https://www.buymeacoffee.com/ashung) or donate [$2.00](https://www.paypal.me/ashung/2) [$5.00](https://www.paypal.me/ashung/5) [$10.00](https://www.paypal.me/ashung/10) via PayPal. 26 | 27 | [使用支付宝或微信扫码打赏](https://ashung.github.io/donate.html) -------------------------------------------------------------------------------- /code.js: -------------------------------------------------------------------------------- 1 | figma.showUI(__html__, { width: 240, height: 230 }); 2 | figma.on('selectionchange', update); 3 | update(); 4 | function update() { 5 | let selection = figma.currentPage.selection; 6 | if (selection.length > 0) { 7 | if (selection.every(layer => layer.type === 'TEXT')) { 8 | figma.ui.postMessage({ 9 | type: 'text', 10 | count: selection.length 11 | }); 12 | } 13 | else if (selection.every(layer => layer.type !== 'TEXT' && layer.fills !== undefined)) { 14 | figma.ui.postMessage({ 15 | type: 'image', 16 | count: selection.length 17 | }); 18 | } 19 | else { 20 | figma.ui.postMessage({ 21 | type: 'message', 22 | text: 'Only support text and shape layer, but don\'t select both of them.' 23 | }); 24 | } 25 | } 26 | else { 27 | figma.ui.postMessage({ 28 | type: 'message', 29 | text: 'Select at least 1 text or shape layer.' 30 | }); 31 | } 32 | } 33 | figma.ui.onmessage = msg => { 34 | let selection = figma.currentPage.selection; 35 | if (msg.type === 'text') { 36 | const selectedTextNodes = selection.map(item => item); 37 | const fontNames = getFontNamesFromLayers(selectedTextNodes); 38 | const loadFontTasks = fontNames.map(item => figma.loadFontAsync(item)); 39 | Promise.all(loadFontTasks).then(() => { 40 | selectedTextNodes.forEach((node, index) => { 41 | node.characters = msg.data[index]; 42 | }); 43 | }); 44 | } 45 | if (msg.type === 'image') { 46 | let images = msg.data; 47 | selection.forEach((node, index) => { 48 | const paint = { 49 | type: 'IMAGE', 50 | scaleMode: 'FILL', 51 | imageHash: figma.createImage(images[index]).hash 52 | }; 53 | node.fills = [paint]; 54 | }); 55 | } 56 | if (msg.type === 'error') { 57 | figma.notify(msg.data); 58 | } 59 | }; 60 | function getFontNamesFromLayers(layers) { 61 | let fontNames = []; 62 | let _temp = []; 63 | layers.forEach(function (layer) { 64 | let len = layer.characters.length; 65 | for (let i = 0; i < len; i++) { 66 | let fontName = layer.getRangeFontName(i, i + 1); 67 | let fontNameString = fontName.family + ' ' + fontName.style; 68 | if (_temp.indexOf(fontNameString) === -1) { 69 | fontNames.push(fontName); 70 | _temp.push(fontNameString); 71 | } 72 | } 73 | }); 74 | return fontNames; 75 | } 76 | -------------------------------------------------------------------------------- /code.ts: -------------------------------------------------------------------------------- 1 | figma.showUI(__html__, { width: 240, height: 230 }); 2 | 3 | figma.on('selectionchange', update); 4 | 5 | update(); 6 | 7 | function update(): void { 8 | 9 | let selection = figma.currentPage.selection; 10 | 11 | if (selection.length > 0) { 12 | if (selection.every(layer => layer.type === 'TEXT')) { 13 | figma.ui.postMessage({ 14 | type: 'text', 15 | count: selection.length 16 | }); 17 | } 18 | else if (selection.every(layer => layer.type !== 'TEXT' && ( layer).fills !== undefined)) { 19 | figma.ui.postMessage({ 20 | type: 'image', 21 | count: selection.length 22 | }); 23 | } 24 | else { 25 | figma.ui.postMessage({ 26 | type: 'message', 27 | text: 'Only support text and shape layer, but don\'t select both of them.' 28 | }); 29 | } 30 | } else { 31 | figma.ui.postMessage({ 32 | type: 'message', 33 | text: 'Select at least 1 text or shape layer.' 34 | }); 35 | } 36 | } 37 | 38 | figma.ui.onmessage = msg => { 39 | let selection = figma.currentPage.selection; 40 | // Text 41 | if (msg.type === 'text') { 42 | const selectedTextNodes = selection.map(item => ( item)); 43 | const fontNames = getFontNamesFromLayers(selectedTextNodes); 44 | const loadFontTasks = fontNames.map(item => figma.loadFontAsync(item)); 45 | Promise.all(loadFontTasks).then(() => { 46 | selectedTextNodes.forEach((node, index) => { 47 | node.characters = msg.data[index]; 48 | }); 49 | }); 50 | } 51 | // Image 52 | if (msg.type === 'image') { 53 | let images = msg.data; 54 | selection.forEach((node, index) => { 55 | const paint: ImagePaint = { 56 | type: 'IMAGE', 57 | scaleMode: 'FILL', 58 | imageHash: figma.createImage(images[index]).hash 59 | }; 60 | ( node).fills = [paint]; 61 | }); 62 | } 63 | // Error 64 | if (msg.type === 'error') { 65 | figma.notify(msg.data); 66 | } 67 | }; 68 | 69 | function getFontNamesFromLayers(layers: TextNode []): FontName [] { 70 | let fontNames = []; 71 | let _temp = []; 72 | layers.forEach(function(layer) { 73 | let len = layer.characters.length; 74 | for (let i = 0; i < len; i++) { 75 | let fontName = layer.getRangeFontName(i, i+1); 76 | let fontNameString = ( fontName).family + ' ' + ( fontName).style; 77 | if (_temp.indexOf(fontNameString) === -1) { 78 | fontNames.push(fontName); 79 | _temp.push(fontNameString); 80 | } 81 | } 82 | }); 83 | return fontNames; 84 | } 85 | -------------------------------------------------------------------------------- /data_from_local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ashung/data-from-local-figma/728abd3f7d173b800e414fcd2b6497d584af8acc/data_from_local.png -------------------------------------------------------------------------------- /figma.d.ts: -------------------------------------------------------------------------------- 1 | // Figma Plugin API version 1, update 13 2 | 3 | declare global { 4 | // Global variable with Figma's plugin API. 5 | const figma: PluginAPI 6 | const __html__: string 7 | 8 | interface PluginAPI { 9 | readonly apiVersion: "1.0.0" 10 | readonly command: string 11 | readonly viewport: ViewportAPI 12 | closePlugin(message?: string): void 13 | 14 | notify(message: string, options?: NotificationOptions): NotificationHandler 15 | 16 | showUI(html: string, options?: ShowUIOptions): void 17 | readonly ui: UIAPI 18 | 19 | readonly clientStorage: ClientStorageAPI 20 | 21 | getNodeById(id: string): BaseNode | null 22 | getStyleById(id: string): BaseStyle | null 23 | 24 | readonly root: DocumentNode 25 | currentPage: PageNode 26 | 27 | on(type: "selectionchange" | "currentpagechange" | "close", callback: () => void): void 28 | once(type: "selectionchange" | "currentpagechange" | "close", callback: () => void): void 29 | off(type: "selectionchange" | "currentpagechange" | "close", callback: () => void): void 30 | 31 | readonly mixed: unique symbol 32 | 33 | createRectangle(): RectangleNode 34 | createLine(): LineNode 35 | createEllipse(): EllipseNode 36 | createPolygon(): PolygonNode 37 | createStar(): StarNode 38 | createVector(): VectorNode 39 | createText(): TextNode 40 | createFrame(): FrameNode 41 | createComponent(): ComponentNode 42 | createPage(): PageNode 43 | createSlice(): SliceNode 44 | /** 45 | * [DEPRECATED]: This API often fails to create a valid boolean operation. Use figma.union, figma.subtract, figma.intersect and figma.exclude instead. 46 | */ 47 | createBooleanOperation(): BooleanOperationNode 48 | 49 | createPaintStyle(): PaintStyle 50 | createTextStyle(): TextStyle 51 | createEffectStyle(): EffectStyle 52 | createGridStyle(): GridStyle 53 | 54 | // The styles are returned in the same order as displayed in the UI. Only 55 | // local styles are returned. Never styles from team library. 56 | getLocalPaintStyles(): PaintStyle[] 57 | getLocalTextStyles(): TextStyle[] 58 | getLocalEffectStyles(): EffectStyle[] 59 | getLocalGridStyles(): GridStyle[] 60 | 61 | importComponentByKeyAsync(key: string): Promise 62 | importStyleByKeyAsync(key: string): Promise 63 | 64 | listAvailableFontsAsync(): Promise 65 | loadFontAsync(fontName: FontName): Promise 66 | readonly hasMissingFont: boolean 67 | 68 | createNodeFromSvg(svg: string): FrameNode 69 | 70 | createImage(data: Uint8Array): Image 71 | getImageByHash(hash: string): Image 72 | 73 | group(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): GroupNode 74 | flatten(nodes: ReadonlyArray, parent?: BaseNode & ChildrenMixin, index?: number): VectorNode 75 | 76 | union(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): BooleanOperationNode 77 | subtract(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): BooleanOperationNode 78 | intersect(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): BooleanOperationNode 79 | exclude(nodes: ReadonlyArray, parent: BaseNode & ChildrenMixin, index?: number): BooleanOperationNode 80 | } 81 | 82 | interface ClientStorageAPI { 83 | getAsync(key: string): Promise 84 | setAsync(key: string, value: any): Promise 85 | } 86 | 87 | interface NotificationOptions { 88 | timeout?: number 89 | } 90 | 91 | interface NotificationHandler { 92 | cancel: () => void 93 | } 94 | 95 | interface ShowUIOptions { 96 | visible?: boolean 97 | width?: number 98 | height?: number 99 | } 100 | 101 | interface UIPostMessageOptions { 102 | origin?: string 103 | } 104 | 105 | interface OnMessageProperties { 106 | origin: string 107 | } 108 | 109 | type MessageEventHandler = (pluginMessage: any, props: OnMessageProperties) => void 110 | 111 | interface UIAPI { 112 | show(): void 113 | hide(): void 114 | resize(width: number, height: number): void 115 | close(): void 116 | 117 | postMessage(pluginMessage: any, options?: UIPostMessageOptions): void 118 | onmessage: MessageEventHandler | undefined 119 | on(type: "message", callback: MessageEventHandler): void 120 | once(type: "message", callback: MessageEventHandler): void 121 | off(type: "message", callback: MessageEventHandler): void 122 | } 123 | 124 | interface ViewportAPI { 125 | center: Vector 126 | zoom: number 127 | scrollAndZoomIntoView(nodes: ReadonlyArray): void 128 | readonly bounds: Rect 129 | } 130 | 131 | //////////////////////////////////////////////////////////////////////////////// 132 | // Datatypes 133 | 134 | type Transform = [ 135 | [number, number, number], 136 | [number, number, number] 137 | ] 138 | 139 | interface Vector { 140 | readonly x: number 141 | readonly y: number 142 | } 143 | 144 | interface Rect { 145 | readonly x: number 146 | readonly y: number 147 | readonly width: number 148 | readonly height: number 149 | } 150 | 151 | interface RGB { 152 | readonly r: number 153 | readonly g: number 154 | readonly b: number 155 | } 156 | 157 | interface RGBA { 158 | readonly r: number 159 | readonly g: number 160 | readonly b: number 161 | readonly a: number 162 | } 163 | 164 | interface FontName { 165 | readonly family: string 166 | readonly style: string 167 | } 168 | 169 | type TextCase = "ORIGINAL" | "UPPER" | "LOWER" | "TITLE" 170 | 171 | type TextDecoration = "NONE" | "UNDERLINE" | "STRIKETHROUGH" 172 | 173 | interface ArcData { 174 | readonly startingAngle: number 175 | readonly endingAngle: number 176 | readonly innerRadius: number 177 | } 178 | 179 | interface ShadowEffect { 180 | readonly type: "DROP_SHADOW" | "INNER_SHADOW" 181 | readonly color: RGBA 182 | readonly offset: Vector 183 | readonly radius: number 184 | readonly visible: boolean 185 | readonly blendMode: BlendMode 186 | } 187 | 188 | interface BlurEffect { 189 | readonly type: "LAYER_BLUR" | "BACKGROUND_BLUR" 190 | readonly radius: number 191 | readonly visible: boolean 192 | } 193 | 194 | type Effect = ShadowEffect | BlurEffect 195 | 196 | type ConstraintType = "MIN" | "CENTER" | "MAX" | "STRETCH" | "SCALE" 197 | 198 | interface Constraints { 199 | readonly horizontal: ConstraintType 200 | readonly vertical: ConstraintType 201 | } 202 | 203 | interface ColorStop { 204 | readonly position: number 205 | readonly color: RGBA 206 | } 207 | 208 | interface ImageFilters { 209 | readonly exposure?: number 210 | readonly contrast?: number 211 | readonly saturation?: number 212 | readonly temperature?: number 213 | readonly tint?: number 214 | readonly highlights?: number 215 | readonly shadows?: number 216 | } 217 | 218 | interface SolidPaint { 219 | readonly type: "SOLID" 220 | readonly color: RGB 221 | 222 | readonly visible?: boolean 223 | readonly opacity?: number 224 | readonly blendMode?: BlendMode 225 | } 226 | 227 | interface GradientPaint { 228 | readonly type: "GRADIENT_LINEAR" | "GRADIENT_RADIAL" | "GRADIENT_ANGULAR" | "GRADIENT_DIAMOND" 229 | readonly gradientTransform: Transform 230 | readonly gradientStops: ReadonlyArray 231 | 232 | readonly visible?: boolean 233 | readonly opacity?: number 234 | readonly blendMode?: BlendMode 235 | } 236 | 237 | interface ImagePaint { 238 | readonly type: "IMAGE" 239 | readonly scaleMode: "FILL" | "FIT" | "CROP" | "TILE" 240 | readonly imageHash: string | null 241 | readonly imageTransform?: Transform // setting for "CROP" 242 | readonly scalingFactor?: number // setting for "TILE" 243 | readonly filters?: ImageFilters 244 | 245 | readonly visible?: boolean 246 | readonly opacity?: number 247 | readonly blendMode?: BlendMode 248 | } 249 | 250 | type Paint = SolidPaint | GradientPaint | ImagePaint 251 | 252 | interface Guide { 253 | readonly axis: "X" | "Y" 254 | readonly offset: number 255 | } 256 | 257 | interface RowsColsLayoutGrid { 258 | readonly pattern: "ROWS" | "COLUMNS" 259 | readonly alignment: "MIN" | "MAX" | "STRETCH" | "CENTER" 260 | readonly gutterSize: number 261 | 262 | readonly count: number // Infinity when "Auto" is set in the UI 263 | readonly sectionSize?: number // Not set for alignment: "STRETCH" 264 | readonly offset?: number // Not set for alignment: "CENTER" 265 | 266 | readonly visible?: boolean 267 | readonly color?: RGBA 268 | } 269 | 270 | interface GridLayoutGrid { 271 | readonly pattern: "GRID" 272 | readonly sectionSize: number 273 | 274 | readonly visible?: boolean 275 | readonly color?: RGBA 276 | } 277 | 278 | type LayoutGrid = RowsColsLayoutGrid | GridLayoutGrid 279 | 280 | interface ExportSettingsConstraints { 281 | readonly type: "SCALE" | "WIDTH" | "HEIGHT" 282 | readonly value: number 283 | } 284 | 285 | interface ExportSettingsImage { 286 | readonly format: "JPG" | "PNG" 287 | readonly contentsOnly?: boolean // defaults to true 288 | readonly suffix?: string 289 | readonly constraint?: ExportSettingsConstraints 290 | } 291 | 292 | interface ExportSettingsSVG { 293 | readonly format: "SVG" 294 | readonly contentsOnly?: boolean // defaults to true 295 | readonly suffix?: string 296 | readonly svgOutlineText?: boolean // defaults to true 297 | readonly svgIdAttribute?: boolean // defaults to false 298 | readonly svgSimplifyStroke?: boolean // defaults to true 299 | } 300 | 301 | interface ExportSettingsPDF { 302 | readonly format: "PDF" 303 | readonly contentsOnly?: boolean // defaults to true 304 | readonly suffix?: string 305 | } 306 | 307 | type ExportSettings = ExportSettingsImage | ExportSettingsSVG | ExportSettingsPDF 308 | 309 | type WindingRule = "NONZERO" | "EVENODD" 310 | 311 | interface VectorVertex { 312 | readonly x: number 313 | readonly y: number 314 | readonly strokeCap?: StrokeCap 315 | readonly strokeJoin?: StrokeJoin 316 | readonly cornerRadius?: number 317 | readonly handleMirroring?: HandleMirroring 318 | } 319 | 320 | interface VectorSegment { 321 | readonly start: number 322 | readonly end: number 323 | readonly tangentStart?: Vector // Defaults to { x: 0, y: 0 } 324 | readonly tangentEnd?: Vector // Defaults to { x: 0, y: 0 } 325 | } 326 | 327 | interface VectorRegion { 328 | readonly windingRule: WindingRule 329 | readonly loops: ReadonlyArray> 330 | } 331 | 332 | interface VectorNetwork { 333 | readonly vertices: ReadonlyArray 334 | readonly segments: ReadonlyArray 335 | readonly regions?: ReadonlyArray // Defaults to [] 336 | } 337 | 338 | interface VectorPath { 339 | readonly windingRule: WindingRule | "NONE" 340 | readonly data: string 341 | } 342 | 343 | type VectorPaths = ReadonlyArray 344 | 345 | interface LetterSpacing { 346 | readonly value: number 347 | readonly unit: "PIXELS" | "PERCENT" 348 | } 349 | 350 | type LineHeight = { 351 | readonly value: number 352 | readonly unit: "PIXELS" | "PERCENT" 353 | } | { 354 | readonly unit: "AUTO" 355 | } 356 | 357 | type BlendMode = 358 | "PASS_THROUGH" | 359 | "NORMAL" | 360 | "DARKEN" | 361 | "MULTIPLY" | 362 | "LINEAR_BURN" | 363 | "COLOR_BURN" | 364 | "LIGHTEN" | 365 | "SCREEN" | 366 | "LINEAR_DODGE" | 367 | "COLOR_DODGE" | 368 | "OVERLAY" | 369 | "SOFT_LIGHT" | 370 | "HARD_LIGHT" | 371 | "DIFFERENCE" | 372 | "EXCLUSION" | 373 | "HUE" | 374 | "SATURATION" | 375 | "COLOR" | 376 | "LUMINOSITY" 377 | 378 | interface Font { 379 | fontName: FontName 380 | } 381 | 382 | type Reaction = { action: Action, trigger: Trigger } 383 | 384 | type Action = 385 | { readonly type: "BACK" | "CLOSE" } | 386 | { readonly type: "URL", url: string } | 387 | { readonly type: "NODE" 388 | readonly destinationId: string | null 389 | readonly navigation: Navigation 390 | readonly transition: Transition | null 391 | readonly preserveScrollPosition: boolean 392 | 393 | // Only present if navigation == "OVERLAY" and the destination uses 394 | // overlay position type "RELATIVE" 395 | readonly overlayRelativePosition?: Vector 396 | } 397 | 398 | interface SimpleTransition { 399 | readonly type: "DISSOLVE" | "SMART_ANIMATE" 400 | readonly easing: Easing 401 | readonly duration: number 402 | } 403 | 404 | interface DirectionalTransition { 405 | readonly type: "MOVE_IN" | "MOVE_OUT" | "PUSH" | "SLIDE_IN" | "SLIDE_OUT" 406 | readonly direction: "LEFT" | "RIGHT" | "TOP" | "BOTTOM" 407 | readonly matchLayers: boolean 408 | 409 | readonly easing: Easing 410 | readonly duration: number 411 | } 412 | 413 | type Transition = SimpleTransition | DirectionalTransition 414 | 415 | type Trigger = 416 | { readonly type: "ON_CLICK" | "ON_HOVER" | "ON_PRESS" | "ON_DRAG" } | 417 | { readonly type: "AFTER_TIMEOUT", readonly timeout: number } | 418 | { readonly type: "MOUSE_ENTER" | "MOUSE_LEAVE" | "MOUSE_UP" | "MOUSE_DOWN" 419 | readonly delay: number 420 | } 421 | 422 | type Navigation = "NAVIGATE" | "SWAP" | "OVERLAY" 423 | 424 | interface Easing { 425 | readonly type: "EASE_IN" | "EASE_OUT" | "EASE_IN_AND_OUT" | "LINEAR" 426 | } 427 | 428 | type OverflowDirection = "NONE" | "HORIZONTAL" | "VERTICAL" | "BOTH" 429 | 430 | type OverlayPositionType = "CENTER" | "TOP_LEFT" | "TOP_CENTER" | "TOP_RIGHT" | "BOTTOM_LEFT" | "BOTTOM_CENTER" | "BOTTOM_RIGHT" | "MANUAL" 431 | 432 | type OverlayBackground = 433 | { readonly type: "NONE" } | 434 | { readonly type: "SOLID_COLOR", readonly color: RGBA } 435 | 436 | type OverlayBackgroundInteraction = "NONE" | "CLOSE_ON_CLICK_OUTSIDE" 437 | 438 | //////////////////////////////////////////////////////////////////////////////// 439 | // Mixins 440 | 441 | interface BaseNodeMixin { 442 | readonly id: string 443 | readonly parent: (BaseNode & ChildrenMixin) | null 444 | name: string // Note: setting this also sets `autoRename` to false on TextNodes 445 | readonly removed: boolean 446 | toString(): string 447 | remove(): void 448 | 449 | getPluginData(key: string): string 450 | setPluginData(key: string, value: string): void 451 | 452 | // Namespace is a string that must be at least 3 alphanumeric characters, and should 453 | // be a name related to your plugin. Other plugins will be able to read this data. 454 | getSharedPluginData(namespace: string, key: string): string 455 | setSharedPluginData(namespace: string, key: string, value: string): void 456 | } 457 | 458 | interface SceneNodeMixin { 459 | visible: boolean 460 | locked: boolean 461 | } 462 | 463 | interface ChildrenMixin { 464 | readonly children: ReadonlyArray 465 | 466 | appendChild(child: SceneNode): void 467 | insertChild(index: number, child: SceneNode): void 468 | 469 | findChildren(callback?: (node: SceneNode) => boolean): SceneNode[] 470 | findChild(callback: (node: SceneNode) => boolean): SceneNode | null 471 | 472 | /** 473 | * If you only need to search immediate children, it is much faster 474 | * to call node.children.filter(callback) or node.findChildren(callback) 475 | */ 476 | findAll(callback?: (node: SceneNode) => boolean): SceneNode[] 477 | 478 | /** 479 | * If you only need to search immediate children, it is much faster 480 | * to call node.children.find(callback) or node.findChild(callback) 481 | */ 482 | findOne(callback: (node: SceneNode) => boolean): SceneNode | null 483 | } 484 | 485 | interface ConstraintMixin { 486 | constraints: Constraints 487 | } 488 | 489 | interface LayoutMixin { 490 | readonly absoluteTransform: Transform 491 | relativeTransform: Transform 492 | x: number 493 | y: number 494 | rotation: number // In degrees 495 | 496 | readonly width: number 497 | readonly height: number 498 | constrainProportions: boolean 499 | 500 | layoutAlign: "MIN" | "CENTER" | "MAX" | "STRETCH" // applicable only inside auto-layout frames 501 | 502 | resize(width: number, height: number): void 503 | resizeWithoutConstraints(width: number, height: number): void 504 | } 505 | 506 | interface BlendMixin { 507 | opacity: number 508 | blendMode: BlendMode 509 | isMask: boolean 510 | effects: ReadonlyArray 511 | effectStyleId: string 512 | } 513 | 514 | interface ContainerMixin { 515 | expanded: boolean 516 | backgrounds: ReadonlyArray // DEPRECATED: use 'fills' instead 517 | backgroundStyleId: string // DEPRECATED: use 'fillStyleId' instead 518 | } 519 | 520 | type StrokeCap = "NONE" | "ROUND" | "SQUARE" | "ARROW_LINES" | "ARROW_EQUILATERAL" 521 | type StrokeJoin = "MITER" | "BEVEL" | "ROUND" 522 | type HandleMirroring = "NONE" | "ANGLE" | "ANGLE_AND_LENGTH" 523 | 524 | interface GeometryMixin { 525 | fills: ReadonlyArray | PluginAPI['mixed'] 526 | strokes: ReadonlyArray 527 | strokeWeight: number 528 | strokeMiterLimit: number 529 | strokeAlign: "CENTER" | "INSIDE" | "OUTSIDE" 530 | strokeCap: StrokeCap | PluginAPI['mixed'] 531 | strokeJoin: StrokeJoin | PluginAPI['mixed'] 532 | dashPattern: ReadonlyArray 533 | fillStyleId: string | PluginAPI['mixed'] 534 | strokeStyleId: string 535 | outlineStroke(): VectorNode | null 536 | } 537 | 538 | interface CornerMixin { 539 | cornerRadius: number | PluginAPI['mixed'] 540 | cornerSmoothing: number 541 | } 542 | 543 | interface RectangleCornerMixin { 544 | topLeftRadius: number 545 | topRightRadius: number 546 | bottomLeftRadius: number 547 | bottomRightRadius: number 548 | } 549 | 550 | interface ExportMixin { 551 | exportSettings: ReadonlyArray 552 | exportAsync(settings?: ExportSettings): Promise // Defaults to PNG format 553 | } 554 | 555 | interface RelaunchableMixin { 556 | setRelaunchData(relaunchData: { [command: string]: /* description */ string }): void 557 | } 558 | 559 | interface ReactionMixin { 560 | readonly reactions: ReadonlyArray 561 | } 562 | 563 | interface DefaultShapeMixin extends 564 | BaseNodeMixin, SceneNodeMixin, ReactionMixin, 565 | BlendMixin, GeometryMixin, LayoutMixin, 566 | ExportMixin, RelaunchableMixin { 567 | } 568 | 569 | interface DefaultFrameMixin extends 570 | BaseNodeMixin, SceneNodeMixin, ReactionMixin, 571 | ChildrenMixin, ContainerMixin, 572 | GeometryMixin, CornerMixin, RectangleCornerMixin, 573 | BlendMixin, ConstraintMixin, LayoutMixin, 574 | ExportMixin, RelaunchableMixin { 575 | 576 | layoutMode: "NONE" | "HORIZONTAL" | "VERTICAL" 577 | counterAxisSizingMode: "FIXED" | "AUTO" // applicable only if layoutMode != "NONE" 578 | horizontalPadding: number // applicable only if layoutMode != "NONE" 579 | verticalPadding: number // applicable only if layoutMode != "NONE" 580 | itemSpacing: number // applicable only if layoutMode != "NONE" 581 | 582 | layoutGrids: ReadonlyArray 583 | gridStyleId: string 584 | clipsContent: boolean 585 | guides: ReadonlyArray 586 | 587 | overflowDirection: OverflowDirection 588 | numberOfFixedChildren: number 589 | 590 | readonly overlayPositionType: OverlayPositionType 591 | readonly overlayBackground: OverlayBackground 592 | readonly overlayBackgroundInteraction: OverlayBackgroundInteraction 593 | } 594 | 595 | //////////////////////////////////////////////////////////////////////////////// 596 | // Nodes 597 | 598 | interface DocumentNode extends BaseNodeMixin { 599 | readonly type: "DOCUMENT" 600 | 601 | readonly children: ReadonlyArray 602 | 603 | appendChild(child: PageNode): void 604 | insertChild(index: number, child: PageNode): void 605 | findChildren(callback?: (node: PageNode) => boolean): Array 606 | findChild(callback: (node: PageNode) => boolean): PageNode | null 607 | 608 | /** 609 | * If you only need to search immediate children, it is much faster 610 | * to call node.children.filter(callback) or node.findChildren(callback) 611 | */ 612 | findAll(callback?: (node: PageNode | SceneNode) => boolean): Array 613 | 614 | /** 615 | * If you only need to search immediate children, it is much faster 616 | * to call node.children.find(callback) or node.findChild(callback) 617 | */ 618 | findOne(callback: (node: PageNode | SceneNode) => boolean): PageNode | SceneNode | null 619 | } 620 | 621 | interface PageNode extends BaseNodeMixin, ChildrenMixin, ExportMixin, RelaunchableMixin { 622 | 623 | readonly type: "PAGE" 624 | clone(): PageNode 625 | 626 | guides: ReadonlyArray 627 | selection: ReadonlyArray 628 | selectedTextRange: { node: TextNode, start: number, end: number } | null 629 | 630 | backgrounds: ReadonlyArray 631 | 632 | readonly prototypeStartNode: FrameNode | GroupNode | ComponentNode | InstanceNode | null 633 | } 634 | 635 | interface FrameNode extends DefaultFrameMixin { 636 | readonly type: "FRAME" 637 | clone(): FrameNode 638 | } 639 | 640 | interface GroupNode extends 641 | BaseNodeMixin, SceneNodeMixin, ReactionMixin, 642 | ChildrenMixin, ContainerMixin, BlendMixin, 643 | LayoutMixin, ExportMixin, RelaunchableMixin { 644 | 645 | readonly type: "GROUP" 646 | clone(): GroupNode 647 | } 648 | 649 | interface SliceNode extends 650 | BaseNodeMixin, SceneNodeMixin, LayoutMixin, 651 | ExportMixin, RelaunchableMixin { 652 | 653 | readonly type: "SLICE" 654 | clone(): SliceNode 655 | } 656 | 657 | interface RectangleNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin, RectangleCornerMixin { 658 | readonly type: "RECTANGLE" 659 | clone(): RectangleNode 660 | } 661 | 662 | interface LineNode extends DefaultShapeMixin, ConstraintMixin { 663 | readonly type: "LINE" 664 | clone(): LineNode 665 | } 666 | 667 | interface EllipseNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin { 668 | readonly type: "ELLIPSE" 669 | clone(): EllipseNode 670 | arcData: ArcData 671 | } 672 | 673 | interface PolygonNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin { 674 | readonly type: "POLYGON" 675 | clone(): PolygonNode 676 | pointCount: number 677 | } 678 | 679 | interface StarNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin { 680 | readonly type: "STAR" 681 | clone(): StarNode 682 | pointCount: number 683 | innerRadius: number 684 | } 685 | 686 | interface VectorNode extends DefaultShapeMixin, ConstraintMixin, CornerMixin { 687 | readonly type: "VECTOR" 688 | clone(): VectorNode 689 | vectorNetwork: VectorNetwork 690 | vectorPaths: VectorPaths 691 | handleMirroring: HandleMirroring | PluginAPI['mixed'] 692 | } 693 | 694 | interface TextNode extends DefaultShapeMixin, ConstraintMixin { 695 | readonly type: "TEXT" 696 | clone(): TextNode 697 | readonly hasMissingFont: boolean 698 | textAlignHorizontal: "LEFT" | "CENTER" | "RIGHT" | "JUSTIFIED" 699 | textAlignVertical: "TOP" | "CENTER" | "BOTTOM" 700 | textAutoResize: "NONE" | "WIDTH_AND_HEIGHT" | "HEIGHT" 701 | paragraphIndent: number 702 | paragraphSpacing: number 703 | autoRename: boolean 704 | 705 | textStyleId: string | PluginAPI['mixed'] 706 | fontSize: number | PluginAPI['mixed'] 707 | fontName: FontName | PluginAPI['mixed'] 708 | textCase: TextCase | PluginAPI['mixed'] 709 | textDecoration: TextDecoration | PluginAPI['mixed'] 710 | letterSpacing: LetterSpacing | PluginAPI['mixed'] 711 | lineHeight: LineHeight | PluginAPI['mixed'] 712 | 713 | characters: string 714 | insertCharacters(start: number, characters: string, useStyle?: "BEFORE" | "AFTER"): void 715 | deleteCharacters(start: number, end: number): void 716 | 717 | getRangeFontSize(start: number, end: number): number | PluginAPI['mixed'] 718 | setRangeFontSize(start: number, end: number, value: number): void 719 | getRangeFontName(start: number, end: number): FontName | PluginAPI['mixed'] 720 | setRangeFontName(start: number, end: number, value: FontName): void 721 | getRangeTextCase(start: number, end: number): TextCase | PluginAPI['mixed'] 722 | setRangeTextCase(start: number, end: number, value: TextCase): void 723 | getRangeTextDecoration(start: number, end: number): TextDecoration | PluginAPI['mixed'] 724 | setRangeTextDecoration(start: number, end: number, value: TextDecoration): void 725 | getRangeLetterSpacing(start: number, end: number): LetterSpacing | PluginAPI['mixed'] 726 | setRangeLetterSpacing(start: number, end: number, value: LetterSpacing): void 727 | getRangeLineHeight(start: number, end: number): LineHeight | PluginAPI['mixed'] 728 | setRangeLineHeight(start: number, end: number, value: LineHeight): void 729 | getRangeFills(start: number, end: number): Paint[] | PluginAPI['mixed'] 730 | setRangeFills(start: number, end: number, value: Paint[]): void 731 | getRangeTextStyleId(start: number, end: number): string | PluginAPI['mixed'] 732 | setRangeTextStyleId(start: number, end: number, value: string): void 733 | getRangeFillStyleId(start: number, end: number): string | PluginAPI['mixed'] 734 | setRangeFillStyleId(start: number, end: number, value: string): void 735 | } 736 | 737 | interface ComponentNode extends DefaultFrameMixin { 738 | readonly type: "COMPONENT" 739 | clone(): ComponentNode 740 | 741 | createInstance(): InstanceNode 742 | description: string 743 | readonly remote: boolean 744 | readonly key: string // The key to use with "importComponentByKeyAsync" 745 | } 746 | 747 | interface InstanceNode extends DefaultFrameMixin { 748 | readonly type: "INSTANCE" 749 | clone(): InstanceNode 750 | masterComponent: ComponentNode 751 | scaleFactor: number 752 | } 753 | 754 | interface BooleanOperationNode extends DefaultShapeMixin, ChildrenMixin, CornerMixin { 755 | readonly type: "BOOLEAN_OPERATION" 756 | clone(): BooleanOperationNode 757 | booleanOperation: "UNION" | "INTERSECT" | "SUBTRACT" | "EXCLUDE" 758 | 759 | expanded: boolean 760 | } 761 | 762 | type BaseNode = 763 | DocumentNode | 764 | PageNode | 765 | SceneNode 766 | 767 | type SceneNode = 768 | SliceNode | 769 | FrameNode | 770 | GroupNode | 771 | ComponentNode | 772 | InstanceNode | 773 | BooleanOperationNode | 774 | VectorNode | 775 | StarNode | 776 | LineNode | 777 | EllipseNode | 778 | PolygonNode | 779 | RectangleNode | 780 | TextNode 781 | 782 | type NodeType = 783 | "DOCUMENT" | 784 | "PAGE" | 785 | "SLICE" | 786 | "FRAME" | 787 | "GROUP" | 788 | "COMPONENT" | 789 | "INSTANCE" | 790 | "BOOLEAN_OPERATION" | 791 | "VECTOR" | 792 | "STAR" | 793 | "LINE" | 794 | "ELLIPSE" | 795 | "POLYGON" | 796 | "RECTANGLE" | 797 | "TEXT" 798 | 799 | //////////////////////////////////////////////////////////////////////////////// 800 | // Styles 801 | type StyleType = "PAINT" | "TEXT" | "EFFECT" | "GRID" 802 | 803 | interface BaseStyle { 804 | readonly id: string 805 | readonly type: StyleType 806 | name: string 807 | description: string 808 | remote: boolean 809 | readonly key: string // The key to use with "importStyleByKeyAsync" 810 | remove(): void 811 | } 812 | 813 | interface PaintStyle extends BaseStyle { 814 | type: "PAINT" 815 | paints: ReadonlyArray 816 | } 817 | 818 | interface TextStyle extends BaseStyle { 819 | type: "TEXT" 820 | fontSize: number 821 | textDecoration: TextDecoration 822 | fontName: FontName 823 | letterSpacing: LetterSpacing 824 | lineHeight: LineHeight 825 | paragraphIndent: number 826 | paragraphSpacing: number 827 | textCase: TextCase 828 | } 829 | 830 | interface EffectStyle extends BaseStyle { 831 | type: "EFFECT" 832 | effects: ReadonlyArray 833 | } 834 | 835 | interface GridStyle extends BaseStyle { 836 | type: "GRID" 837 | layoutGrids: ReadonlyArray 838 | } 839 | 840 | //////////////////////////////////////////////////////////////////////////////// 841 | // Other 842 | 843 | interface Image { 844 | readonly hash: string 845 | getBytesAsync(): Promise 846 | } 847 | } // declare global 848 | 849 | export {} 850 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Data From Local", 3 | "id": "759249283654441170", 4 | "api": "1.0.0", 5 | "main": "code.js", 6 | "ui": "ui.html" 7 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "removeComments": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /ui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 90 | 91 | 92 | 93 | 97 | 98 | 310 | 311 | --------------------------------------------------------------------------------