Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions .eslintrc.js

This file was deleted.

1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
save-exact=true
only-built-dependencies=esbuild
11 changes: 11 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "lf"
}
11 changes: 0 additions & 11 deletions .prettierrc.js

This file was deleted.

91 changes: 91 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Run Plugin API - Figma Plugin

Figma上でPlugin APIのJavaScriptコードを直接実行できるプラグイン。Monaco Editorベースのコードエディタを内蔵。

## アーキテクチャ

```
┌─────────────────────────────────┐
│ Figma Desktop App │
│ │
│ ┌───────────┐ postMessage ┌──────────┐
│ │Plugin Code│◄────────────►│ UI │
│ │(sandbox) │ onmessage │(iframe) │
│ │ │ │ │
│ │ code.ts │ │ React │
│ │ figma API │ │ Monaco │
│ │ access │ │ Zustand │
│ └───────────┘ └──────────┘
│ │
└─────────────────────────────────┘
```

- **Plugin Code (sandbox)**: `src/code.ts` - Figma APIにアクセス可能。ユーザーコードの実行、オプション永続化を担当
- **UI (iframe)**: `src/ui/` - React + Monaco Editorによるコードエディタ UI

## ビルド・開発コマンド

```bash
pnpm install # 依存インストール
pnpm dev # 開発ビルド(watch)
pnpm build # プロダクションビルド
pnpm lint # lint実行
```

## ディレクトリ構造

```
src/
├── @types/
│ ├── common.d.ts # 共通型定義(Options, PluginMessage等)
│ ├── assets.d.ts # アセット型宣言(.dts, .svg)
│ └── global.d.ts # グローバル型宣言(ts)
├── code.ts # Plugin Code エントリポイント
├── constants.ts # 定数(CDN_URL等)
├── defaultOptions.ts # エディタのデフォルト設定
└── ui/
├── main.tsx # UI エントリポイント
├── App.tsx # ルートコンポーネント
├── Store.ts # 状態管理
├── index.html # HTMLテンプレート
├── styles.ts # グローバルスタイル
├── themeList.ts # エディタテーマ一覧
├── assets/ # 静的アセット
└── components/ # UIコンポーネント
manifest.json # Figmaプラグインマニフェスト
```

## Figma Plugin 固有の制約

- Plugin Code はFigmaのsandboxで実行され、DOM/ブラウザAPIにアクセス不可
- UI はiframe内で実行され、Figma APIに直接アクセス不可
- 両者の通信は `figma.ui.postMessage()` / `figma.ui.onmessage` で行う
- `ui.html` には全JS/CSSがインライン化される(ビルド時に生成)
- `manifest.json` で `main`(Plugin Code)と `ui`(UI HTML)のパスを指定

## コーディング規約

- TypeScript strict mode
- セミコロンなし(`semi: false`)
- シングルクォート
- 末尾コンマなし
- `@emotion/react` による CSS-in-JS
- パスエイリアス: `@/` → `src/`

## デバッグ方法

1. Figma Desktop Appで `Plugins > Development > Import plugin from manifest...` からmanifest.jsonを読み込む
2. `pnpm dev` でwatchビルドを起動
3. Plugin Codeのログ: Figmaのメニュー `Plugins > Development > Open Console` で確認
4. UIのログ: プラグインウィンドウを右クリック → `Inspect Element` でDevToolsを開く

## 状態管理

UIの状態管理にはZustandを使用。Store.tsで定義されたストアがアプリ全体の状態を管理する。

## 注意点

- Monaco EditorはCDNからロードされる
- エディタテーマのJSONもCDNから取得(`constants.ts` の `CDN_URL`)
- ユーザーコードの実行には `new Function()` を使用(`figma` オブジェクトを引数として渡す)
- オプションは `figma.clientStorage` に永続化される
36 changes: 36 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import reactPlugin from 'eslint-plugin-react'

export default tseslint.config(
{
ignores: ['dist/', 'node_modules/', 'src/ui/assets/types/']
},
js.configs.recommended,
...tseslint.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
ecmaFeatures: {
jsx: true
}
}
},
plugins: {
react: reactPlugin
},
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'react/no-unknown-property': ['error', { ignore: ['css'] }],
'react/react-in-jsx-scope': 'off'
},
settings: {
react: {
version: 'detect'
}
}
}
)
63 changes: 27 additions & 36 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,37 @@
"name": "figma-plugin-run-plugin-api",
"version": "1.0.0",
"author": "Ryo Nakae",
"type": "module",
"dependencies": {
"@emotion/react": "11.13.3",
"@emotion/react": "11.14.0",
"@monaco-editor/react": "4.7.0",
"monaco-editor": "0.52.2",
"monaco-editor": "0.55.1",
"monaco-themes": "0.4.4",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-hotkeys-hook": "3.4.7",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-hotkeys-hook": "4.6.2",
"ress": "4.0.0",
"unstated-next": "1.1.0"
"zustand": "5.0.11"
},
"devDependencies": {
"@emotion/babel-plugin": "11.12.0",
"@figma/plugin-typings": "1.109.0",
"@svgr/webpack": "6.5.1",
"@types/node": "17.0.45",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.2",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
"css-loader": "6.11.0",
"@figma/plugin-typings": "1.123.0",
"@types/node": "22.15.0",
"@types/react": "18.3.28",
"@types/react-dom": "18.3.7",
"@vitejs/plugin-react": "5.1.4",
"@eslint/js": "10.0.1",
"csstype": "3.1.3",
"dts-generator": "3.0.0",
"eslint": "8.57.0",
"eslint-config-prettier": "8.10.0",
"eslint-import-resolver-typescript": "2.7.1",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-react": "7.35.2",
"html-inline-script-webpack-plugin": "2.0.3",
"html-webpack-plugin": "5.6.0",
"eslint": "10.0.3",
"eslint-plugin-react": "7.37.5",
"husky": "9.1.5",
"lint-staged": "15.2.10",
"prettier": "2.8.8",
"style-loader": "3.3.4",
"terser-webpack-plugin": "5.3.10",
"ts-loader": "9.5.1",
"typescript": "4.9.5",
"typescript-json-schema": "0.55.0",
"webpack": "5.94.0",
"webpack-cli": "4.10.0"
"prettier": "3.8.1",
"typescript": "5.9.3",
"typescript-eslint": "8.57.0",
"vite": "7.3.1",
"vite-plugin-singlefile": "2.3.0",
"vite-plugin-svgr": "4.5.0"
},
"license": "MIT",
"lint-staged": {
Expand All @@ -50,12 +41,12 @@
]
},
"scripts": {
"build": "webpack --mode production",
"dev": "webpack -w",
"generate-editor-options": "typescript-json-schema node_modules/monaco-editor/esm/vs/editor/editor.api.d.ts editor.IStandaloneEditorConstructionOptions --ignoreErrors --excludePrivate --out src/ui/assets/types/editorOptions.schema.json",
"generate-figma-dts": "dts-generator --project node_modules/@figma/plugin-typings/ --out src/ui/assets/types/figma.d.ts",
"lint:fix": "npm run lint:prettier --write && npm run lint:eslint --fix",
"lint:eslint": "eslint --ext .ts,.tsx .",
"build": "vite build && vite build --config vite.config.code.ts",
"build:ui": "vite build",
"build:code": "vite build --config vite.config.code.ts",
"dev": "vite build --watch & vite build --config vite.config.code.ts --watch & wait",
"lint:fix": "prettier --write . --ignore-path .prettierignore && eslint --fix .",
"lint:eslint": "eslint .",
"lint:prettier": "prettier --check . --ignore-path .prettierignore",
"prepare": "husky || true"
}
Expand Down
Loading