tellfigma 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +271 -0
- package/bin/cli.js +98 -0
- package/dist/chrome.d.ts +20 -0
- package/dist/chrome.d.ts.map +1 -0
- package/dist/chrome.js +114 -0
- package/dist/chrome.js.map +1 -0
- package/dist/figma.d.ts +12 -0
- package/dist/figma.d.ts.map +1 -0
- package/dist/figma.js +156 -0
- package/dist/figma.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +411 -0
- package/dist/prompt.js.map +1 -0
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +104 -0
- package/dist/prompts.js.map +1 -0
- package/dist/tools.d.ts +3 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +457 -0
- package/dist/tools.js.map +1 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Directive Labs
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# tellfigma
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/tellfigma)
|
|
4
|
+
[](https://www.npmjs.com/package/tellfigma)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
|
|
8
|
+
<!-- 🎬 Add a demo GIF here — this is the #1 thing that makes repos go viral on GitHub/Twitter/Reddit -->
|
|
9
|
+
<!--  -->
|
|
10
|
+
|
|
11
|
+
**MCP server that gives AI full control over Figma. Create and edit designs from natural language.**
|
|
12
|
+
|
|
13
|
+
> Figma's MCP reads designs. **tellfigma writes them.**
|
|
14
|
+
|
|
15
|
+
One command. No Figma API key. No plugin. Works with Claude Desktop, Claude Code, VS Code Copilot, Cursor, Windsurf — any MCP client.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx tellfigma
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why tellfigma?
|
|
24
|
+
|
|
25
|
+
Every other Figma AI tool is **read-only** or **sandboxed**. tellfigma is the only MCP server that uses **Chrome DevTools Protocol** to give AI full read/write access to Figma's Plugin API — the same API that Figma plugins use, but without the sandbox.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
┌─────────────────┐ MCP (stdio) ┌──────────┐ Chrome DevTools ┌──────────┐
|
|
29
|
+
│ Claude Desktop │ ◄────────────────────► │ tellfigma │ ◄────────────────► │ Chrome │
|
|
30
|
+
│ Claude Code │ │ (local) │ Protocol (CDP) │ + Figma │
|
|
31
|
+
│ VS Code Copilot │ └──────────┘ └──────────┘
|
|
32
|
+
│ Cursor / etc. │
|
|
33
|
+
└─────────────────┘
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### The AI + Figma Loop
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
① tellfigma AI ──────► Figma "Design a dashboard"
|
|
40
|
+
② Figma MCP Server Figma ──────► Code "Build this design"
|
|
41
|
+
③ Claude Code to Figma Code ──────► Figma "Capture this UI"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
tellfigma is **step ①** — the missing piece that lets AI create and modify Figma designs from scratch.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### 1. Run tellfigma
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx tellfigma
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A Chrome window opens. Sign into Figma and open a design file.
|
|
57
|
+
|
|
58
|
+
### 2. Add to your AI app
|
|
59
|
+
|
|
60
|
+
<details>
|
|
61
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
62
|
+
|
|
63
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"tellfigma": {
|
|
69
|
+
"command": "npx",
|
|
70
|
+
"args": ["-y", "tellfigma"]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Restart Claude Desktop.
|
|
77
|
+
</details>
|
|
78
|
+
|
|
79
|
+
<details>
|
|
80
|
+
<summary><strong>Claude Code</strong></summary>
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
claude mcp add tellfigma -- npx -y tellfigma
|
|
84
|
+
```
|
|
85
|
+
</details>
|
|
86
|
+
|
|
87
|
+
<details>
|
|
88
|
+
<summary><strong>VS Code (GitHub Copilot)</strong></summary>
|
|
89
|
+
|
|
90
|
+
Add to `.vscode/mcp.json`:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"servers": {
|
|
95
|
+
"tellfigma": {
|
|
96
|
+
"type": "stdio",
|
|
97
|
+
"command": "npx",
|
|
98
|
+
"args": ["-y", "tellfigma"]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
</details>
|
|
104
|
+
|
|
105
|
+
<details>
|
|
106
|
+
<summary><strong>Cursor</strong></summary>
|
|
107
|
+
|
|
108
|
+
Add to `~/.cursor/mcp.json`:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"tellfigma": {
|
|
114
|
+
"command": "npx",
|
|
115
|
+
"args": ["-y", "tellfigma"]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
</details>
|
|
121
|
+
|
|
122
|
+
<details>
|
|
123
|
+
<summary><strong>Windsurf</strong></summary>
|
|
124
|
+
|
|
125
|
+
Add to `~/.windsurf/mcp.json`:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"tellfigma": {
|
|
131
|
+
"command": "npx",
|
|
132
|
+
"args": ["-y", "tellfigma"]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
</details>
|
|
138
|
+
|
|
139
|
+
### 3. Start designing
|
|
140
|
+
|
|
141
|
+
Tell your AI what to create:
|
|
142
|
+
|
|
143
|
+
> "Design a modern login page with email and password fields, a sign-in button, and a 'Forgot password?' link"
|
|
144
|
+
|
|
145
|
+
> "Take a screenshot and give me feedback on the spacing and hierarchy"
|
|
146
|
+
|
|
147
|
+
> "Find all text nodes on this page and make them use Inter Semi Bold"
|
|
148
|
+
|
|
149
|
+
> "Create a card component with a subtle shadow, 16px padding, and 12px border radius"
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Features
|
|
154
|
+
|
|
155
|
+
### 16 MCP Tools
|
|
156
|
+
|
|
157
|
+
| Tool | What it does |
|
|
158
|
+
|------|-------------|
|
|
159
|
+
| `execute_figma_code` | Run any JavaScript with full `figma` Plugin API access |
|
|
160
|
+
| `take_screenshot` | Capture what's on screen — the AI sees your canvas |
|
|
161
|
+
| `read_selection` | Deep inspect fills, strokes, effects, layout, fonts, children |
|
|
162
|
+
| `get_page_context` | Page name, selection, top-level frames |
|
|
163
|
+
| `select_nodes` | Find and select by name or type (FRAME, TEXT, COMPONENT, etc.) |
|
|
164
|
+
| `list_components` | List all components and component sets |
|
|
165
|
+
| `get_styles` | List local paint, text, and effect styles |
|
|
166
|
+
| `get_variables` | List design tokens — colors, numbers, strings |
|
|
167
|
+
| `export_node` | Export as PNG, SVG, JPG, or PDF |
|
|
168
|
+
| `duplicate_node` | Clone with offset and count for grids |
|
|
169
|
+
| `undo` / `redo` | Roll back or redo with configurable steps |
|
|
170
|
+
| `zoom_to` | Zoom to selection, all nodes, or a specific node |
|
|
171
|
+
| `navigate` | Open a URL (e.g., a specific Figma file) |
|
|
172
|
+
| `click` | Click at coordinates on the page |
|
|
173
|
+
| `get_snapshot` | Accessibility tree for understanding UI structure |
|
|
174
|
+
|
|
175
|
+
### Built-in Design Intelligence
|
|
176
|
+
|
|
177
|
+
The AI receives a comprehensive system prompt with:
|
|
178
|
+
|
|
179
|
+
- **Figma Plugin API reference** — every method, property, and pattern
|
|
180
|
+
- **Design recipes** — buttons, cards, inputs, navbars the AI can compose
|
|
181
|
+
- **Design system defaults** — 8px spacing scale, color palette, type scale, shadows
|
|
182
|
+
- **Smart error recovery** — hints for common Figma API mistakes (fonts, layout ordering, null nodes)
|
|
183
|
+
- **Auto-reconnect** — drops CDP? Picks back up on the next tool call
|
|
184
|
+
|
|
185
|
+
### Design From Your Codebase
|
|
186
|
+
|
|
187
|
+
Using VS Code, Cursor, or Claude Code? The AI already has your project files. Ask it to design screens that match your actual codebase:
|
|
188
|
+
|
|
189
|
+
> "Design a settings page that matches my app's design system"
|
|
190
|
+
|
|
191
|
+
It reads your `tailwind.config.ts`, `globals.css`, component files — then creates Figma designs using your exact colors, fonts, spacing, and patterns. Works with **Tailwind**, **shadcn/ui**, **MUI**, **Chakra**, and any component library.
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
┌──────────┐ reads ┌───────────┐ designs ┌────────┐
|
|
195
|
+
│ Your Code │ ────────────► │ AI Agent │ ────────────► │ Figma │
|
|
196
|
+
│ (editor) │ tailwind, │ (Copilot/ │ your exact │ canvas │
|
|
197
|
+
│ │ components │ Claude) │ tokens │ │
|
|
198
|
+
└──────────┘ └───────────┘ └────────┘
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## How It Works
|
|
204
|
+
|
|
205
|
+
1. `npx tellfigma` launches Chrome with `--remote-debugging-port=9222` and a dedicated profile (`~/.tellfigma-chrome-profile`)
|
|
206
|
+
2. The MCP server starts on stdio
|
|
207
|
+
3. When an AI calls `execute_figma_code`, tellfigma connects via CDP, finds the Figma tab, and runs JavaScript through `Runtime.evaluate`
|
|
208
|
+
4. Screenshots use `Page.captureScreenshot` — real browser screenshots, not API renders
|
|
209
|
+
5. The AI receives a system prompt with the full Figma Plugin API reference
|
|
210
|
+
|
|
211
|
+
Chrome runs with its own profile so it doesn't interfere with your normal browsing.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Compared to Alternatives
|
|
216
|
+
|
|
217
|
+
| | tellfigma | Figma MCP (Dev Mode) | Claude Code to Figma | Plugin + WebSocket |
|
|
218
|
+
|---|---|---|---|---|
|
|
219
|
+
| **Creates designs** | ✅ | ❌ Read-only | ❌ Captures existing UI | ✅ |
|
|
220
|
+
| **Edits designs** | ✅ | ❌ | ❌ One-time import | ✅ |
|
|
221
|
+
| **Real screenshots** | ✅ | ✅ | N/A | ❌ |
|
|
222
|
+
| **Any MCP client** | ✅ | ✅ | ❌ Claude only | ❌ |
|
|
223
|
+
| **No API key** | ✅ | ❌ Token required | ❌ OAuth required | ✅ |
|
|
224
|
+
| **No plugin install** | ✅ | ❌ | ❌ | ❌ |
|
|
225
|
+
| **Full Plugin API** | ✅ | ❌ | ❌ | Partial |
|
|
226
|
+
| **Setup** | `npx tellfigma` | Config + token | Server + OAuth | Plugin + WS + MCP |
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Options
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
npx tellfigma [options]
|
|
234
|
+
|
|
235
|
+
--port <number> Chrome debug port (default: 9222)
|
|
236
|
+
--help, -h Show help
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Requirements
|
|
240
|
+
|
|
241
|
+
- **Node.js 18+**
|
|
242
|
+
- **Google Chrome** (or Chromium)
|
|
243
|
+
- Any MCP-compatible AI app
|
|
244
|
+
|
|
245
|
+
## Troubleshooting
|
|
246
|
+
|
|
247
|
+
**"No Figma tab found"** — Open a Figma design file in the Chrome window that tellfigma launched. Make sure the URL contains `figma.com/design` or `figma.com/file`.
|
|
248
|
+
|
|
249
|
+
**"Chrome debug port didn't become available"** — Another process may be using port 9222. Try `npx tellfigma --port 9333` or close other Chrome debug instances.
|
|
250
|
+
|
|
251
|
+
**"Connection lost, reconnecting..."** — This is normal. tellfigma auto-reconnects on the next tool call. If it persists, reload the Figma tab.
|
|
252
|
+
|
|
253
|
+
**Font errors** — Always call `await figma.loadFontAsync({ family, style })` before setting `.characters` on a text node. Inter "Semi Bold" has a space (not "SemiBold").
|
|
254
|
+
|
|
255
|
+
**Code executed but nothing appeared** — Make sure you're calling `figma.currentPage.appendChild(node)` after creating frames/shapes. New nodes aren't visible until appended.
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Contributing
|
|
260
|
+
|
|
261
|
+
PRs welcome. Please open an issue first for major changes.
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT — [Directive Labs](https://directivelabs.com)
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
<p align="center">
|
|
270
|
+
<a href="https://tellfigma.com">tellfigma.com</a> · <a href="https://directivelabs.com">Directive Labs</a> · <a href="https://github.com/mrpeterperez/tellfigma">GitHub</a>
|
|
271
|
+
</p>
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ============================================================
|
|
4
|
+
// tellfigma CLI — Control Figma from any AI app. One command.
|
|
5
|
+
// ============================================================
|
|
6
|
+
// Usage:
|
|
7
|
+
// npx tellfigma # Start with defaults
|
|
8
|
+
// npx tellfigma --port 9222 # Custom Chrome debug port
|
|
9
|
+
// npx tellfigma --no-launch # Don't launch Chrome (connect to existing)
|
|
10
|
+
// ============================================================
|
|
11
|
+
|
|
12
|
+
import { startServer } from '../dist/index.js';
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
|
|
16
|
+
// Parse args
|
|
17
|
+
let port = 9222;
|
|
18
|
+
let showHelp = false;
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
if (args[i] === '--port' && args[i + 1]) {
|
|
22
|
+
port = parseInt(args[i + 1], 10);
|
|
23
|
+
i++;
|
|
24
|
+
} else if (args[i] === '--help' || args[i] === '-h') {
|
|
25
|
+
showHelp = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (showHelp) {
|
|
30
|
+
console.log(`
|
|
31
|
+
tellfigma — Control Figma from any AI app. One command.
|
|
32
|
+
|
|
33
|
+
Usage:
|
|
34
|
+
npx tellfigma [options]
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--port <number> Chrome debug port (default: 9222)
|
|
38
|
+
--help, -h Show this help
|
|
39
|
+
|
|
40
|
+
Setup:
|
|
41
|
+
1. Run this command: npx tellfigma
|
|
42
|
+
2. Log into Figma in the Chrome window that opens
|
|
43
|
+
3. Open a design file
|
|
44
|
+
4. Add tellfigma to your AI app's MCP config:
|
|
45
|
+
|
|
46
|
+
Claude Desktop (~/.claude/claude_desktop_config.json):
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"tellfigma": {
|
|
50
|
+
"command": "npx",
|
|
51
|
+
"args": ["-y", "tellfigma"]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
Claude Code:
|
|
57
|
+
claude mcp add tellfigma -- npx -y tellfigma
|
|
58
|
+
|
|
59
|
+
VS Code (.vscode/mcp.json):
|
|
60
|
+
{
|
|
61
|
+
"servers": {
|
|
62
|
+
"tellfigma": {
|
|
63
|
+
"type": "stdio",
|
|
64
|
+
"command": "npx",
|
|
65
|
+
"args": ["-y", "tellfigma"]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
5. Chat with your AI — it now controls Figma!
|
|
71
|
+
|
|
72
|
+
Examples:
|
|
73
|
+
"Create a card component with a shadow and rounded corners"
|
|
74
|
+
"Design a login page with email and password fields"
|
|
75
|
+
"Find all text on this page and list their font sizes"
|
|
76
|
+
|
|
77
|
+
More info: https://github.com/mrpeterperez/tellfigma
|
|
78
|
+
`);
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ASCII banner
|
|
83
|
+
process.stderr.write(`
|
|
84
|
+
╔═══════════════════════════════════════════════════╗
|
|
85
|
+
║ ║
|
|
86
|
+
║ ▀█▀ █▀▀ █ █ █▀▀ █ █▀▀ █▄ ▄█ █▀█ ║
|
|
87
|
+
║ █ █▀▀ █ █ █▀ █ █ █ █ ▀ █ █▀█ ║
|
|
88
|
+
║ ▀ ▀▀▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ║
|
|
89
|
+
║ ║
|
|
90
|
+
║ Control Figma from any AI app. One command. ║
|
|
91
|
+
║ ║
|
|
92
|
+
╚═══════════════════════════════════════════════════╝
|
|
93
|
+
`);
|
|
94
|
+
|
|
95
|
+
startServer(port).catch((err) => {
|
|
96
|
+
process.stderr.write(`\n[tellfigma] Error: ${err.message}\n`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
});
|
package/dist/chrome.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
export interface ChromeConnection {
|
|
3
|
+
port: number;
|
|
4
|
+
process: ChildProcess | null;
|
|
5
|
+
/** Whether we launched Chrome or connected to existing */
|
|
6
|
+
launched: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Launch Chrome with remote debugging enabled, or connect to existing.
|
|
10
|
+
* Uses a separate user profile so normal Chrome is untouched.
|
|
11
|
+
*/
|
|
12
|
+
export declare function launchChrome(port?: number): Promise<ChromeConnection>;
|
|
13
|
+
/** Find the Figma tab among Chrome's open pages */
|
|
14
|
+
export declare function findFigmaTab(port: number): Promise<{
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
url: string;
|
|
18
|
+
webSocketDebuggerUrl: string;
|
|
19
|
+
} | null>;
|
|
20
|
+
//# sourceMappingURL=chrome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../src/chrome.ts"],"names":[],"mappings":"AAIA,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAuExE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAChC,IAAI,GAAE,MAAqB,GAC1B,OAAO,CAAC,gBAAgB,CAAC,CAiC3B;AAED,mDAAmD;AACnD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB,EAAE,MAAM,CAAC;CAC9B,GAAG,IAAI,CAAC,CA8BR"}
|
package/dist/chrome.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Chrome Launcher — Launch & connect to Chrome with CDP
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { spawn, execSync } from 'node:child_process';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { homedir, platform } from 'node:os';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
const DEFAULT_PORT = 9222;
|
|
9
|
+
const DEBUG_PROFILE = join(homedir(), '.tellfigma-chrome-profile');
|
|
10
|
+
/** Get Chrome path based on OS */
|
|
11
|
+
function getChromePath() {
|
|
12
|
+
const os = platform();
|
|
13
|
+
if (os === 'darwin') {
|
|
14
|
+
const paths = [
|
|
15
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
16
|
+
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
|
|
17
|
+
];
|
|
18
|
+
for (const p of paths) {
|
|
19
|
+
if (existsSync(p))
|
|
20
|
+
return p;
|
|
21
|
+
}
|
|
22
|
+
throw new Error('Chrome not found. Install Google Chrome or set CHROME_PATH environment variable.');
|
|
23
|
+
}
|
|
24
|
+
if (os === 'win32') {
|
|
25
|
+
const paths = [
|
|
26
|
+
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
27
|
+
'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
|
28
|
+
];
|
|
29
|
+
for (const p of paths) {
|
|
30
|
+
if (existsSync(p))
|
|
31
|
+
return p;
|
|
32
|
+
}
|
|
33
|
+
throw new Error('Chrome not found. Install Google Chrome or set CHROME_PATH.');
|
|
34
|
+
}
|
|
35
|
+
// Linux
|
|
36
|
+
try {
|
|
37
|
+
return execSync('which google-chrome || which google-chrome-stable || which chromium-browser')
|
|
38
|
+
.toString()
|
|
39
|
+
.trim()
|
|
40
|
+
.split('\n')[0];
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error('Chrome not found. Install google-chrome or set CHROME_PATH environment variable.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Check if a debug port is already active */
|
|
47
|
+
async function isPortActive(port) {
|
|
48
|
+
try {
|
|
49
|
+
const res = await fetch(`http://127.0.0.1:${port}/json/version`);
|
|
50
|
+
return res.ok;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** Wait for Chrome debug port to become available */
|
|
57
|
+
async function waitForPort(port, timeoutMs = 15000) {
|
|
58
|
+
const start = Date.now();
|
|
59
|
+
while (Date.now() - start < timeoutMs) {
|
|
60
|
+
if (await isPortActive(port))
|
|
61
|
+
return;
|
|
62
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
63
|
+
}
|
|
64
|
+
throw new Error(`Chrome debug port ${port} didn't become available within ${timeoutMs / 1000}s`);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Launch Chrome with remote debugging enabled, or connect to existing.
|
|
68
|
+
* Uses a separate user profile so normal Chrome is untouched.
|
|
69
|
+
*/
|
|
70
|
+
export async function launchChrome(port = DEFAULT_PORT) {
|
|
71
|
+
// Check if Chrome is already running with debug port
|
|
72
|
+
if (await isPortActive(port)) {
|
|
73
|
+
log(`Connected to existing Chrome on port ${port}`);
|
|
74
|
+
return { port, process: null, launched: false };
|
|
75
|
+
}
|
|
76
|
+
const chromePath = process.env.CHROME_PATH || getChromePath();
|
|
77
|
+
log(`Launching Chrome with debug port ${port}...`);
|
|
78
|
+
const child = spawn(chromePath, [
|
|
79
|
+
`--remote-debugging-port=${port}`,
|
|
80
|
+
`--user-data-dir=${DEBUG_PROFILE}`,
|
|
81
|
+
'--no-first-run',
|
|
82
|
+
'--no-default-browser-check',
|
|
83
|
+
'https://www.figma.com',
|
|
84
|
+
], {
|
|
85
|
+
stdio: 'ignore',
|
|
86
|
+
detached: true,
|
|
87
|
+
});
|
|
88
|
+
// Don't let Chrome block our process exit
|
|
89
|
+
child.unref();
|
|
90
|
+
// Wait for debug port
|
|
91
|
+
await waitForPort(port);
|
|
92
|
+
log(`Chrome running on port ${port}`);
|
|
93
|
+
return { port, process: child, launched: true };
|
|
94
|
+
}
|
|
95
|
+
/** Find the Figma tab among Chrome's open pages */
|
|
96
|
+
export async function findFigmaTab(port) {
|
|
97
|
+
try {
|
|
98
|
+
const res = await fetch(`http://127.0.0.1:${port}/json`);
|
|
99
|
+
const pages = (await res.json());
|
|
100
|
+
// Find the Figma design tab (not just figma.com homepage)
|
|
101
|
+
const figmaTab = pages.find((p) => p.type === 'page' && p.url.includes('figma.com/design')) ||
|
|
102
|
+
pages.find((p) => p.type === 'page' && p.url.includes('figma.com/file')) ||
|
|
103
|
+
pages.find((p) => p.type === 'page' && p.url.includes('figma.com/board')) ||
|
|
104
|
+
pages.find((p) => p.type === 'page' && p.url.includes('figma.com'));
|
|
105
|
+
return figmaTab || null;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function log(msg) {
|
|
112
|
+
process.stderr.write(`[tellfigma] ${msg}\n`);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=chrome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../src/chrome.ts"],"names":[],"mappings":"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"}
|
package/dist/figma.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import CDP from 'chrome-remote-interface';
|
|
2
|
+
export declare function log(msg: string): void;
|
|
3
|
+
export declare function setChromePort(port: number): void;
|
|
4
|
+
export declare function ensureConnected(): Promise<CDP.Client>;
|
|
5
|
+
export declare function executeFigmaCode(code: string): Promise<string>;
|
|
6
|
+
export declare function takeScreenshot(): Promise<{
|
|
7
|
+
base64: string;
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function getPageInfo(): Promise<string>;
|
|
12
|
+
//# sourceMappingURL=figma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figma.d.ts","sourceRoot":"","sources":["../src/figma.ts"],"names":[],"mappings":"AAMA,OAAO,GAAG,MAAM,yBAAyB,CAAC;AAS1C,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,QAE9B;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,QAEzC;AAID,wBAAsB,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CA+C3D;AAID,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgDpE;AAID,wBAAsB,cAAc,IAAI,OAAO,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAqBD;AAID,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CA0BnD"}
|