0.0.1 • Published 5 months ago

figwire v0.0.1

Weekly downloads
-
License
ISC
Repository
github
Last release
5 months ago

Figwire

Figwire is a lightweight, TypeScript-friendly library for seamless communication between Figma plugins and their UI.

Inspired by Hono's RPC and figma-await-ipc, Figwire provides a structured way to define APIs for both plugin and UI sides while maintaining strong type safety.

TLDR;

Figwire keeps Figma plugin communication simple, type-safe, and predictable.

  • Define methods using defineApi.
  • Call them from the other side using client<T>.
  • Import from figwire/plugin in plugin code and figwire/ui in ui code.
  • Ensure TypeScript knows about available methods via type exports.

Usage

General idea

Figwire revolves around two core elements:

  • defineApi – Defines methods executed on the plugin (or UI) side.
  • client – A client to call those methods from the opposite side.

The key rule is simple:

  • In UI code, import from figwire/ui.
  • In plugin code, import from figwire/plugin.

Example:

// plugin.ts
import { defineApi, client } from 'figwire/plugin';
// ui.ts
import { defineApi, client } from 'figwire/ui';

Typings

The client is not inherently aware of the API methods (defineApi).

To enable TypeScript to recognize available methods, we explicitly define and export types from the plugin (or UI) side.

Step 1: Define and export API type in the plugin

// plugin.ts
const pluginApi = defineApi({
  greet: (name: string) => `Hello ${name}!`
});

export type PluginAPI = typeof pluginApi;

Step 2: Import and use the API type in the UI

// ui.ts
import type { PluginAPI } from '../plugin/plugin';
import { client } from 'figwire/ui';

const pluginApiClient = client<PluginAPI>();

This pattern works both ways, so UI can also define an API that the plugin can call.

Examples

Requesting plugin methods from UI

Plugin side

import { defineApi } from 'figwire/plugin';

const pluginApi = defineApi({
  greet: (name: string) => `Hello ${name}!`
});

export type PluginAPI = typeof pluginApi;

UI side

import { client } from 'figwire/ui';
import type { PluginAPI } from './plugin';

(async () => {
  const pluginApiClient = client<PluginAPI>();
  const greeting = await pluginApiClient.greet('Johnny Jeep');

  console.log(greeting); // "Hello Johnny Jeep!"
})();

Requesting UI methods from plugin

UI side

import { defineApi } from 'figwire/ui';

const uiApi = defineApi({
  getInputValue: () => (document.getElementById('username') as HTMLInputElement).value
});

export type UIAPI = typeof uiApi;

Plugin side

import { client } from 'figwire/plugin';
import type { UIAPI } from './ui';

(async () => {
  const uiApiClient = client<UIAPI>();
  const username = await uiApiClient.getInputValue();

  console.log(username);
})();

Full example: bidirectional communication

Plugin side

import { defineApi, client } from 'figwire/plugin';
import type { UIAPI } from './ui';

const pluginApi = defineApi({
  cloneNode: (copies: number) => {
    // Clone a node in Figma
    return { message: 'Node successfully cloned.' };
  }
});

export type PluginAPI = typeof pluginApi;

(async () => {
  const uiApiClient = client<UIAPI>();
  const username = await uiApiClient.getInputValue();

  console.log(username);
})();

UI side

import { defineApi, client } from 'figwire/ui';
import type { PluginAPI } from './plugin';

const uiApi = defineApi({
  getInputValue: () => (document.getElementById('username') as HTMLInputElement).value
});

export type UIAPI = typeof uiApi;

(async () => {
  const pluginApiClient = client<PluginAPI>();

  document.getElementById('button').addEventListener('click', async () => {
    await pluginApiClient.cloneNode(5);
  });
})();
0.0.1

5 months ago