mcpField
L
lit-and-figma

lit-and-figma

github.com/rodydavis開発6043 回インストール2 週間前に更新
リポジトリ

lit-and-figma について

Lit と TypeScript で Figma プラグインを作る開発者向けのガイドです。AssemblyScript を WASM にコンパイルし、プラグイン UI にインライン化する手順も扱います。

主な機能

  1. 01Lit による Web コンポーネント作成
  2. 02TypeScript と Webpack の設定
  3. 03ローカル Figma プラグインのビルドと読み込み
  4. 04AssemblyScript の WASM コンパイル
  5. 05UI コードへの WASM インライン化

活用シーン

  1. 01キャンバスに矩形を追加するプラグインの作成
  2. 02Figma プラグイン UI での WASM 計算実行

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rodydavis/skills --skill lit-and-figma

インストール後、Claude Code で/lit-and-figmaを入力して読み込まれたか確認します。

詳細

Lit と Figma

この記事では、Lit の Web コンポーネントをセットアップし、それを使って Figma プラグインを作成する方法を説明します。

TLDR 最終的なソースはこちらにあります:こちら。

前提条件

  • Vscode
  • Figma Desktop
  • Node
  • Typescript

はじめに

まず、空のディレクトリを作成し、好きな名前を snake_case で付けます。

mkdir figma_lit_example
cd figma_lit_example
Web のセットアップ

figma_lit_example ディレクトリに移動したら、Figma と Lit をセットアップできます。まずは Node から始めましょう。

npm init -y

これにより、Node プロジェクトの基本設定が行われ、必要なパッケージがインストールされます。次に、設定ファイルを追加します。package.json を開き、以下の内容に置き換えます。

{
  "name": "figma_lit_example",
  "version": "1.0.0",
  "description": "Lit Figma Plugin",
  "dependencies": {
    "lit": "^2.0.0-rc.1"
  },
  "devDependencies": {
    "@figma/plugin-typings": "^1.23.0",
    "html-webpack-inline-source-plugin": "^1.0.0-beta.2",
    "html-webpack-plugin": "^4.3.0",
    "css-loader": "^5.2.4",
    "ts-loader": "^8.0.0",
    "typescript": "^4.2.4",
    "url-loader": "^4.1.1",
    "webpack": "^4.44.1",
    "webpack-cli": "^4.6.0"
  },
  "scripts": {
    "dev": "npx webpack --mode=development --watch",
    "copy": "mkdir -p lit-plugin && cp ./manifest.json ./lit-plugin/manifest.json && cp ./dist/ui.html ./lit-plugin/ui.html && cp ./dist/code.js ./lit-plugin/code.js",
    "build": "npx webpack --mode=production && npm run copy",
    "zip": "npm run build && zip -r lit-plugin.zip lit-plugin"
  },
  "browserslist": [
    "last 1 Chrome versions"
  ],
  "keywords": [],
  "author": "",
  "license": "ISC"
}

これにより、必要なものがすべて追加され、開発環境と本番環境に必要なスクリプトも追加されます。次に、以下を実行します。

npm i

これにより、開始に必要なものがすべてインストールされます。次に、設定ファイルをセットアップします。

touch tsconfig.json
touch webpack.config.ts

これにより、2 つのファイルが作成されます。tsconfig.json を開き、以下の内容を貼り付けます。

{
  "compilerOptions": {
    "target": "es2017",
    "module": "esNext",
    "moduleResolution": "node",
    "lib": ["es2017", "dom", "dom.iterable"],
    "typeRoots": ["./node_modules/@types", "./node_modules/@figma"],
    "declaration": true,
    "sourceMap": true,
    "inlineSources": true,
    "noUnusedLocals": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "experimentalDecorators": true,
    "skipLibCheck": true,
    "strict": true,
    "noImplicitAny": false,
    "outDir": "./lib",
    "baseUrl": "./packages",
    "importHelpers": true,
    "plugins": [
      {
        "name": "ts-lit-plugin",
        "rules": {
          "no-unknown-tag-name": "error",
          "no-unclosed-tag": "error",
          "no-unknown-property": "error",
          "no-unintended-mixed-binding": "error",
          "no-invalid-boolean-binding": "error",
          "no-expressionless-property-binding": "error",
          "no-noncallable-event-binding": "error",
          "no-boolean-in-attribute-binding": "error",
          "no-complex-attribute-binding": "error",
          "no-nullable-attribute-binding": "error",
          "no-incompatible-type-binding": "error",
          "no-invalid-directive-binding": "error",
          "no-incompatible-property-type": "error",
          "no-unknown-property-converter": "error",
          "no-invalid-attribute-name": "error",
          "no-invalid-tag-name": "error",
          "no-unknown-attribute": "off",
          "no-unknown-event": "off",
          "no-unknown-slot": "off",
          "no-invalid-css": "off"
        }
      }
    ]
  },
  "include": ["src/**/*.ts"],
  "references": []
}

これは基本的な TypeScript 設定です。次に、webpack.config.ts を開き、以下の内容を貼り付けます。

const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");

module.exports = (env, argv) => ({
  mode: argv.mode === "production" ? "production" : "development",
  devtool: argv.mode === "production" ? false : "inline-source-map",
  entry: {
    ui: "./src/ui.ts",
    code: "./src/code.ts",
    app: "./src/my-app.ts",
  },
  module: {
    rules: [
      { test: /\.tsx?$/, use: "ts-loader", exclude: /node_modules/ },
      { test: /\.css$/, use: ["style-loader", { loader: "css-loader" }] },
      { test: /\.(png|jpg|gif|webp|svg)$/, loader: "url-loader" },
    ],
  },
  resolve: { extensions: [".ts", ".js"] },
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "dist"),
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, "ui.html"),
      filename: "ui.html",
      inject: true,
      inlineSource: ".(js|css)$",
      chunks: ["ui"],
    }),
    new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
  ],
});

次に、プラグインの UI を作成します。

touch ui.html

/src/ui.html を開き、以下を追加します。

<my-app></my-app>

次に、Figma プラグインのマニフェストファイルが必要です。

touch manifest.json

manifest.json を開き、以下を追加します。

{
  "name": "figma_lit_example",
  "id": "973668777853442323",
  "api": "1.0.0",
  "main": "code.js",
  "ui": "ui.html"
}

次に、Web コンポーネントを作成します。

mkdir src
cd src
touch my-app.ts
touch code.ts
touch ui.ts
cd ..

/src/ui.ts を開き、以下の内容を貼り付けます。

import "./my-app";

/src/my-app.ts を開き、以下の内容を貼り付けます。

import { html, LitElement } from "lit";
import { customElement, query } from "lit/decorators.js";

@customElement("my-app")
export class MyApp extends LitElement {
  @property() amount = "5";
  @query("#count") countInput!: HTMLInputElement;

  render() {
    return html`
      <div>
        <h2>Rectangle Creator</h2>
        <p>Count: <input id="count" value="${this.amount}" /></p>
        <button id="create" @click=${this.create}>Create</button>
        <button id="cancel" @click=${this.cancel}>Cancel</button>
      </div>
    `;
  }

  create() {
    const count = parseInt(this.countInput.value, 10);
    this.sendMessage("create-rectangles", { count });
  }

  cancel() {
    this.sendMessage("cancel");
  }

  private sendMessage(type: string, content: Object = {}) {
    const message = { pluginMessage: { type: type, ...content } };
    parent.postMessage(message, "*");
  }
}

code.ts を開き、以下の内容を貼り付けます。

const options: ShowUIOptions = {
  width: 250,
  height: 200,
};

figma.showUI(__html__, options);

figma.ui.onmessage = msg => {
  switch (msg.type) {
    case 'create-rectangles':
      const nodes: SceneNode[] = [];
      for (let i = 0; i < msg.count; i++) {
        const rect = figma.createRectangle();
        rect.x = i * 150;
        rect.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 } }];
        figma.currentPage.appendChild(rect);
        nodes.push(rect);
      }
      figma.currentPage.selection = nodes;
      figma.viewport.scrollAndZoomIntoView(nodes);
      break;
    default:
      break;
  }

  figma.closePlugin();
};

プラグインのビルド

コードの準備ができたので、プラグインをビルドし、Figma でテストできます。

npm run build
ステップ 1

Figma のデスクトップ版をダウンロードして開きます。

https://www.figma.com/downloads/

ステップ 2

メニューを開き、「Plugins > Manage plugins」に移動します。

README の画像

ステップ 3

プラスアイコンをクリックして、ローカルプラグインを追加します。

README の画像

ボックスをクリックして既存のプラグインにリンクし、ソースコードのビルド後に作成された lit-plugin フォルダーに移動して、manifest.json を選択します。

README の画像

ステップ 4

プラグインを実行するには、「Plugins > Development > figma_lit_example」に移動して起動します。

README の画像

ステップ 5

これでプラグインが起動し、キャンバス上に 5 つの長方形を作成できます。

README の画像

正常に動作すれば、Figma でフォーカスされた状態の新しい長方形がキャンバス上に 5 つ表示されます。

README の画像

WASM のサポート

WebAssembly(https://webassembly.org/)で実行すると効果が期待できる負荷の高い計算がある場合、以下の手順により、可能なときはハードウェアアクセラレーションを利用できます。

AssemblyScript(https://www.assemblyscript.org/)と、WASM を Figma の UI に読み込むために使用する依存関係を追加しましょう。

npm i @assemblyscript/loader
npm i --D assemblyscript js-inline-wasm
npx asinit .

プロンプトで「yes」を選択してプロジェクトファイルを生成し、package.json のスクリプトに以下を追加します。

"asbuild:untouched": "asc assembly/index.ts --target debug",
"asbuild:optimized": "asc assembly/index.ts --target release",
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
"inlinewasm": "inlinewasm build/optimized.wasm --output src/wasm.ts",

WASM に使用するコードは /assembly/index.ts にあります。以下のようになっていることを確認してください。

// The entry file of your WebAssembly module.

export function add(a: i32, b: i32): i32 {
  return a + b;
}

では、WASM モジュールをビルドしましょう。

npm run asbuild

WASM のビルド成果物を git add の対象外にするため、以下を .gitignore に追加します。

build

これにより、build ディレクトリに wasm ファイルと wat ファイルが生成されます。ただし、Figma の UI に読み込むにはインライン化する必要があるため、以下のコマンドを実行して wasm ファイルから js を生成します。

npm run inlinewasm

これにより、以下の内容の src/wasm.ts が生成されます。

const encoded = 'AGFzbQEAAAABBwFgAn9/AX8DAgEABQMBAAAHEAIDYWRkAAAGbWVtb3J5AgAKCQEHACAAIAFqCwAmEHNvdXJjZU1hcHBpbmdVUkwULi9vcHRpbWl6ZWQud2FzbS5tYXA=';
export default new Promise(resolve => {
    const decoded = atob(encoded);
    const len = decoded.length;
    const bytes = new Uint8Array(len);
    for (var i = 0; i < len; i++) {
        bytes[i] = decoded.charCodeAt(i);
    }
    resolve(new Response(bytes, { status: 200, headers: { "Content-Type": "application/wasm" } }));
});

次に、/src/my-app.ts を開き、以下の内容に更新します。

import { html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators.js";

@customElement("my-app")
export class MyApp extends LitElement {
  @property() amount = "5"; // <-- Pass in a value for the number of rectangles to create
  @query("#count") countInput!: HTMLInputElement;

  render() {
    return html`
      <div>
        <h2>Rectangle Creator</h2>
        
        <p>Count: <input id="count" value="${this.amount}" /></p>
        ...
      </div>
    `;
  }
  ...
}

これにより、作成するボックスの数を外部から渡せるようになります。

次に、/src/ui.ts を開き、以下の内容に更新します。

import "./my-app";

import wasm from "./wasm"; // <-- Our WASM file to load

WebAssembly.instantiateStreaming(wasm as Promise<Response>).then((obj) => {
  // @ts-ignore
  const value: number = obj.instance.exports.add(2, 4);
  console.log("return from wasm", value);
  const elem = document.querySelector('my-app')! as HTMLElement;
  elem.setAttribute('amount', `${value}`);
});

これで、プラグインをビルドして Figma で実行すると、WASM の呼び出し結果に応じた数のボックスが作成されます。

まとめ

Figma プラグインの作成について詳しく知りたい場合は、こちら(https://www.figma.com/plugin-docs/intro/)をご覧ください。Lit については、こちら(https://lit.dev/)のドキュメントをご覧ください。

コメント