Skip to content

Instantly share code, notes, and snippets.

@glebsexy
Forked from mattdesl/about.md
Last active March 25, 2020 00:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glebsexy/41288eebff0f8b8bff3ab0714d2481bf to your computer and use it in GitHub Desktop.
Save glebsexy/41288eebff0f8b8bff3ab0714d2481bf to your computer and use it in GitHub Desktop.
Figma Plugin Utility: html <-> script I/O for TypeScript

Figma Plugin Utility: html<->script I/O

Inspired by Electron, a simple event emitter you can use to communicate between your main script and your html UI. Original by @mattdesl, adapted to TypeScript by me.

Webpack guide for Figma plugins: https://www.figma.com/plugin-docs/bundling-webpack/

Install types for events:

npm install @types/events

In your script, i.e. main.ts:

import { script as io } from './io';

// Show the iframe
figma.showUI(__html__);

io.send('start', { hello: 'world' });
io.once('beep', data => {
  console.log('Received', data); // 'boop'
});

And in your ui.ts

import { html as io } from './io';

(async () => {
  // Await the first 'start' message
  const msg = await io.async('start');
  console.log(msg.hello); // 'world'

  // Listen for other events
  io.on('image-bytes', bytes => decodeBytes(bytes));

  // Send data back
  io.send('beep', 'boop');
})();

The interface is a Node EventEmitter, so you can use once(), removeListener, etc.

import { EventEmitter } from 'events'
interface CustomEE extends EventEmitter {
send: { (event: string, data: any): void }
async: (ev: string) => Promise<any>
}
function createInterface(renderer: boolean) {
const emitter = new EventEmitter() as CustomEE
const receive = (result: { event: string | number, data: any }) => {
if (result && result.event) {
emitter.emit(result.event, result.data)
}
}
if (renderer) {
window.onmessage = ev => receive(ev.data.pluginMessage)
} else {
figma.ui.onmessage = data => receive(data)
}
emitter.send = function (event, data) {
if (typeof event !== 'string') {
throw new Error('Expected first argument to be an event name string')
}
const postData = {
event,
data
}
if (renderer) {
window.parent.postMessage({ pluginMessage: postData }, '*')
} else {
figma.ui.postMessage(postData)
}
}
emitter.async = function (ev) {
return new Promise((resolve) => {
this.once(ev, resolve)
})
}
return emitter
}
const isRenderer = typeof figma === 'undefined'
export const html = isRenderer ? createInterface(true) : undefined
export const script = isRenderer ? undefined : createInterface(false)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment