figma-local 1.0.0 → 1.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/plugin/ui.html ADDED
@@ -0,0 +1,285 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <style>
5
+ * { margin: 0; padding: 0; box-sizing: border-box; }
6
+ body {
7
+ font-family: Inter, -apple-system, sans-serif;
8
+ font-size: 11px;
9
+ background: #2c2c2c;
10
+ color: #fff;
11
+ height: 100%;
12
+ display: flex;
13
+ flex-direction: column;
14
+ padding: 10px 12px;
15
+ gap: 6px;
16
+ }
17
+ .row {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: 6px;
21
+ }
22
+ .dot {
23
+ width: 8px;
24
+ height: 8px;
25
+ border-radius: 50%;
26
+ flex-shrink: 0;
27
+ background: #666;
28
+ }
29
+ .dot.connecting { background: #f59e0b; animation: pulse 1.2s infinite; }
30
+ .dot.connected { background: #22c55e; }
31
+ .dot.waiting { background: #666; }
32
+ .dot.error { background: #ef4444; }
33
+ @keyframes pulse {
34
+ 0%, 100% { opacity: 1; }
35
+ 50% { opacity: 0.3; }
36
+ }
37
+ .title { font-weight: 600; font-size: 12px; }
38
+ #status { color: #aaa; }
39
+ #instructions {
40
+ display: none;
41
+ margin-top: 2px;
42
+ padding: 7px 8px;
43
+ background: #1e1e1e;
44
+ border-radius: 5px;
45
+ gap: 4px;
46
+ flex-direction: column;
47
+ }
48
+ #instructions.visible { display: flex; }
49
+ .inst-label { color: #888; font-size: 9px; text-transform: uppercase; letter-spacing: 0.04em; }
50
+ .inst-cmd {
51
+ font-family: 'SF Mono', 'Fira Mono', monospace;
52
+ font-size: 10px;
53
+ color: #7dd3fc;
54
+ word-break: break-all;
55
+ }
56
+ .hint { font-size: 9px; color: #555; }
57
+ </style>
58
+ </head>
59
+ <body>
60
+ <div class="title">Figma Local</div>
61
+ <div class="row">
62
+ <div id="dot" class="dot connecting"></div>
63
+ <span id="status">Connecting...</span>
64
+ </div>
65
+ <div id="instructions">
66
+ <span class="inst-label">Start the CLI in your terminal:</span>
67
+ <span class="inst-cmd">fig connect --safe</span>
68
+ </div>
69
+ <div class="hint" id="hint">Keep this window open</div>
70
+
71
+ <script>
72
+ const PORTS = [3456, 3457, 3458, 3459, 3460];
73
+ let ws = null;
74
+ let reconnectTimer = null;
75
+ let keepaliveTimer = null;
76
+ let firstAttemptDone = false;
77
+ let waitingTimer = null;
78
+ let pendingNonce = null; // holds the challenge nonce until hello is sent
79
+
80
+ const statusEl = document.getElementById('status');
81
+ const dotEl = document.getElementById('dot');
82
+ const instructEl = document.getElementById('instructions');
83
+ const hintEl = document.getElementById('hint');
84
+
85
+ function setStatus(text, state) {
86
+ statusEl.textContent = text;
87
+ dotEl.className = 'dot ' + state;
88
+ }
89
+
90
+ function showInstructions() {
91
+ instructEl.classList.add('visible');
92
+ hintEl.textContent = 'Waiting for CLI — retrying automatically';
93
+ }
94
+
95
+ function hideInstructions() {
96
+ instructEl.classList.remove('visible');
97
+ hintEl.textContent = 'Keep this window open';
98
+ }
99
+
100
+ // Try all ports in parallel, use first successful connection
101
+ function connectParallel() {
102
+ if (ws && ws.readyState === WebSocket.OPEN) return;
103
+
104
+ // Only show "Scanning..." on the very first attempt
105
+ if (!firstAttemptDone) {
106
+ setStatus('Connecting...', 'connecting');
107
+ }
108
+
109
+ let connected = false;
110
+ const attempts = [];
111
+
112
+ PORTS.forEach(port => {
113
+ const url = `ws://127.0.0.1:${port}/plugin`;
114
+ try {
115
+ const socket = new WebSocket(url);
116
+ attempts.push(socket);
117
+
118
+ socket.onopen = () => {
119
+ if (connected) { socket.close(); return; }
120
+ connected = true;
121
+ ws = socket;
122
+
123
+ clearTimeout(waitingTimer);
124
+
125
+ // Close other attempts silently
126
+ attempts.forEach(s => {
127
+ if (s !== socket) {
128
+ s.onclose = null;
129
+ s.onerror = null;
130
+ try { s.close(); } catch (e) {}
131
+ }
132
+ });
133
+
134
+ ws.onmessage = handleMessage;
135
+ ws.onclose = handleClose;
136
+ ws.onerror = handleClose;
137
+
138
+ pendingNonce = null; // reset; will be set when challenge arrives
139
+ hideInstructions();
140
+ setStatus('Authenticating...', 'connecting');
141
+ // Do NOT send hello here — wait for daemon's challenge message,
142
+ // then handleMessage will send hello with the echoed nonce.
143
+ startKeepalive();
144
+ };
145
+
146
+ socket.onclose = () => {};
147
+ socket.onerror = () => {};
148
+ } catch (e) {}
149
+ });
150
+
151
+ // After 1.5s, if still not connected: schedule retry
152
+ setTimeout(() => {
153
+ if (!connected) {
154
+ firstAttemptDone = true;
155
+ attempts.forEach(s => {
156
+ s.onclose = null;
157
+ try { s.close(); } catch (e) {}
158
+ });
159
+
160
+ // Show instructions after the first failed attempt
161
+ setStatus('Waiting for CLI...', 'waiting');
162
+ showInstructions();
163
+
164
+ clearTimeout(reconnectTimer);
165
+ reconnectTimer = setTimeout(connectParallel, 2000);
166
+ }
167
+ }, 1500);
168
+ }
169
+
170
+ function handleMessage(event) {
171
+ try {
172
+ const msg = JSON.parse(event.data);
173
+
174
+ // Receive nonce challenge from daemon — echo it back in hello to authenticate
175
+ if (msg.type === 'challenge' && typeof msg.nonce === 'string') {
176
+ pendingNonce = msg.nonce;
177
+ if (ws && ws.readyState === WebSocket.OPEN) {
178
+ ws.send(JSON.stringify({ type: 'hello', mode: 'plugin', version: '1.3.0', nonce: pendingNonce }));
179
+ setStatus('Connected', 'connected');
180
+ parent.postMessage({ pluginMessage: { type: 'connected' } }, '*');
181
+ }
182
+ return;
183
+ }
184
+
185
+ if (msg.action === 'eval') {
186
+ parent.postMessage({
187
+ pluginMessage: { type: 'eval', id: msg.id, code: msg.code }
188
+ }, '*');
189
+ }
190
+
191
+ // Batch eval support
192
+ if (msg.action === 'eval-batch') {
193
+ parent.postMessage({
194
+ pluginMessage: { type: 'eval-batch', id: msg.id, codes: msg.codes }
195
+ }, '*');
196
+ }
197
+
198
+ // Daemon pings us
199
+ if (msg.action === 'ping') {
200
+ ws.send(JSON.stringify({ type: 'pong', id: msg.id }));
201
+ }
202
+
203
+ // Daemon responds to our ping
204
+ if (msg.type === 'pong') {
205
+ lastPong = Date.now();
206
+ }
207
+ } catch (e) {
208
+ console.error('Message parse error:', e);
209
+ }
210
+ }
211
+
212
+ function handleClose() {
213
+ if (ws && ws.readyState !== WebSocket.OPEN) {
214
+ ws = null;
215
+ firstAttemptDone = false; // reset so next attempt shows "Connecting..."
216
+ setStatus('Disconnected — reconnecting...', 'connecting');
217
+ stopKeepalive();
218
+ parent.postMessage({ pluginMessage: { type: 'disconnected' } }, '*');
219
+ clearTimeout(reconnectTimer);
220
+ reconnectTimer = setTimeout(connectParallel, 200);
221
+ }
222
+ }
223
+
224
+ let lastPong = Date.now();
225
+
226
+ function startKeepalive() {
227
+ stopKeepalive();
228
+ lastPong = Date.now();
229
+ keepaliveTimer = setInterval(() => {
230
+ if (ws && ws.readyState === WebSocket.OPEN) {
231
+ // Allow up to 90s without pong — Figma throttles timers when in background
232
+ if (Date.now() - lastPong > 90000) {
233
+ console.log('No pong in 90s, reconnecting...');
234
+ ws.close();
235
+ return;
236
+ }
237
+ ws.send(JSON.stringify({ type: 'ping' }));
238
+ }
239
+ }, 15000); // Ping every 15s — relaxed to survive background throttling
240
+ }
241
+
242
+ function stopKeepalive() {
243
+ if (keepaliveTimer) {
244
+ clearInterval(keepaliveTimer);
245
+ keepaliveTimer = null;
246
+ }
247
+ }
248
+
249
+ // Legacy function name for compatibility
250
+ function connect() { connectParallel(); }
251
+
252
+ // Receive results from plugin main thread
253
+ window.onmessage = (event) => {
254
+ const msg = event.data.pluginMessage;
255
+ if (!msg) return;
256
+
257
+ if (msg.type === 'result') {
258
+ // Send result back to CLI via WebSocket
259
+ if (ws && ws.readyState === WebSocket.OPEN) {
260
+ ws.send(JSON.stringify({
261
+ type: 'result',
262
+ id: msg.id,
263
+ result: msg.result,
264
+ error: msg.error
265
+ }));
266
+ }
267
+ }
268
+
269
+ // Batch result forwarding (CRITICAL for render-batch stability)
270
+ if (msg.type === 'batch-result') {
271
+ if (ws && ws.readyState === WebSocket.OPEN) {
272
+ ws.send(JSON.stringify({
273
+ type: 'batch-result',
274
+ id: msg.id,
275
+ results: msg.results
276
+ }));
277
+ }
278
+ }
279
+ };
280
+
281
+ // Start connection
282
+ connect();
283
+ </script>
284
+ </body>
285
+ </html>
@@ -0,0 +1,119 @@
1
+ ---
2
+ name: figma-css
3
+ description: |
4
+ Use this skill when the user wants to generate CSS or Tailwind classes from a Figma design element. Triggers on: "generate CSS", "CSS from Figma", "Tailwind classes", "get the styles as code", "convert to CSS", "translate to Tailwind", "what CSS would this be", "style this like the design", "code from Figma". Also use when implementing a UI component and needing the exact CSS properties to match the Figma design. Requires an element to be selected in Figma or a node ID / Figma link.
5
+ allowed-tools:
6
+ - Bash(fig css *)
7
+ - Bash(fig css)
8
+ ---
9
+
10
+ # Figma CSS
11
+
12
+ Generate ready-to-use CSS or Tailwind utility classes from any Figma element.
13
+
14
+ ## Prerequisites
15
+
16
+ The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
17
+
18
+ ## Usage
19
+
20
+ ### CSS from current selection (rem units — default)
21
+
22
+ ```bash
23
+ fig css
24
+ ```
25
+
26
+ **Output example:**
27
+ ```css
28
+ /* card [FRAME] */
29
+ .card {
30
+ width: 20rem;
31
+ height: 12.5rem;
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 0.5rem;
35
+ padding: 1.5rem;
36
+ border-radius: 0.75rem;
37
+ background-color: #ffffff;
38
+ box-shadow: 0rem 0.063rem 0.188rem 0rem rgba(0,0,0,0.1);
39
+ }
40
+ ```
41
+
42
+ ### CSS in px units
43
+
44
+ ```bash
45
+ fig css --px
46
+ ```
47
+
48
+ ### Tailwind utility classes
49
+
50
+ ```bash
51
+ fig css --tailwind
52
+ ```
53
+
54
+ **Output example:**
55
+ ```
56
+ {/* card */}
57
+ className="w-[20rem] h-[12.5rem] flex flex-col gap-[0.5rem] p-[1.5rem] rounded-[0.75rem] bg-[#ffffff] shadow-[0rem_0.063rem_0.188rem_0rem_rgba(0,0,0,0.1)]"
58
+ ```
59
+
60
+ ### CSS from a Figma link
61
+
62
+ ```bash
63
+ fig css --link "https://www.figma.com/design/FILEID/Name?node-id=123-456"
64
+ ```
65
+
66
+ ### CSS for a specific node
67
+
68
+ ```bash
69
+ fig css --node "123:456"
70
+ ```
71
+
72
+ ### Raw JSON output
73
+
74
+ ```bash
75
+ fig css --json
76
+ ```
77
+
78
+ ## What it extracts
79
+
80
+ | CSS Property | Figma Source |
81
+ |-------------|-------------|
82
+ | `width`, `height` | Node dimensions |
83
+ | `display: flex`, `flex-direction` | Auto layout mode |
84
+ | `gap` | Auto layout item spacing |
85
+ | `padding` | Auto layout padding (shorthand when possible) |
86
+ | `justify-content`, `align-items` | Auto layout alignment |
87
+ | `flex-wrap` | Auto layout wrap |
88
+ | `border-radius` | Corner radius (uniform or per-corner) |
89
+ | `background-color` | Solid fills (hex or rgba) |
90
+ | `background` | Gradient fills (linear-gradient) |
91
+ | `border` | Strokes (width + style + color) |
92
+ | `font-family` | Text font name |
93
+ | `font-size` | Text size |
94
+ | `font-weight` | Font style → weight mapping |
95
+ | `font-style` | Italic detection |
96
+ | `line-height` | Text line height (px/rem or unitless) |
97
+ | `letter-spacing` | Text letter spacing (px/rem or em) |
98
+ | `text-align` | Horizontal text alignment |
99
+ | `color` | Text fill color |
100
+ | `box-shadow` | Drop shadows and inner shadows |
101
+ | `opacity` | Layer opacity |
102
+ | `overflow: hidden` | Clip content |
103
+
104
+ ## Workflow: Design to Code
105
+
106
+ 1. Select the element in Figma
107
+ 2. Run `fig css` (or `fig css --tailwind`)
108
+ 3. Copy the output into your component file
109
+ 4. Adjust class names or variable references as needed
110
+ 5. Run `fig verify` to visually compare
111
+
112
+ ## Tips
113
+
114
+ - For text elements, `color` is used instead of `background-color`
115
+ - Padding is automatically collapsed to shorthand when sides match (e.g., `1rem` instead of `1rem 1rem 1rem 1rem`)
116
+ - Use `--px` when your project uses px-based design systems
117
+ - Use `--tailwind` for React/Vue/Svelte projects using Tailwind CSS
118
+ - The generated class name is derived from the Figma layer name (sanitized to kebab-case)
119
+ - Select multiple elements to generate CSS for all of them at once (up to 10)
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: figma-document
3
+ description: |
4
+ Use this skill when the user wants complete documentation of a Figma component, frame, or screen — a full recursive breakdown of every element with all specs (colors, spacing, typography, layout, effects, variable bindings) in a structured format that coding agents can directly use to replicate in code. Triggers on: "document this component", "document this screen", "get full specs", "component documentation", "break down this design", "analyze this component for code", "full spec sheet", "replicate this in code", "all the specs for this", "deep inspect", "complete breakdown". Best for when you need everything about a component in one call — not just the top-level element. Requires an element to be selected in Figma or a node ID / Figma link.
5
+ allowed-tools:
6
+ - Bash(fig document *)
7
+ - Bash(fig document)
8
+ ---
9
+
10
+ # Figma Document
11
+
12
+ Generate complete, recursive component documentation from Figma. Unlike `fig inspect` (which shows one element), `fig document` walks the **entire tree** of children and produces:
13
+
14
+ 1. **Summary** — component name, type, dimensions, total node count
15
+ 2. **Design tokens** — all unique colors, typography, spacing, radii, and shadows used across the tree, with usage counts
16
+ 3. **Component tree** — full recursive JSON of every element with all specs (layout, fills, strokes, typography, effects, variable bindings)
17
+
18
+ All values in both **px and rem** (base 16px).
19
+
20
+ ## Prerequisites
21
+
22
+ The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
23
+
24
+ ## Commands
25
+
26
+ ### Document current selection
27
+ ```bash
28
+ fig document
29
+ ```
30
+
31
+ ### JSON output (best for coding agents)
32
+ ```bash
33
+ fig document --json
34
+ ```
35
+ Returns structured JSON with `summary`, `tokens`, and `tree` — directly parseable for code generation.
36
+
37
+ ### Document a specific node
38
+ ```bash
39
+ fig document --node "123:456"
40
+ ```
41
+
42
+ ### Document from a Figma link
43
+ ```bash
44
+ fig document --link "https://www.figma.com/design/abc/file?node-id=123-456"
45
+ ```
46
+
47
+ ### Tokens only
48
+ ```bash
49
+ fig document --tokens-only
50
+ ```
51
+ Just the design tokens summary (colors, fonts, spacing, radii, shadows) without the full tree.
52
+
53
+ ## Output Structure (JSON mode)
54
+
55
+ ```json
56
+ {
57
+ "summary": {
58
+ "name": "Button",
59
+ "type": "COMPONENT",
60
+ "totalNodes": 5,
61
+ "width": 120,
62
+ "height": 40,
63
+ "isComponent": true
64
+ },
65
+ "tokens": {
66
+ "colors": [
67
+ { "value": "#6366f1", "count": 2 }
68
+ ],
69
+ "typography": [
70
+ { "value": "Inter Medium / 14px", "count": 1 }
71
+ ],
72
+ "spacing": [
73
+ { "value": "8px", "rem": "0.5rem", "count": 1 },
74
+ { "value": "16px", "rem": "1rem", "count": 2 }
75
+ ],
76
+ "radii": [
77
+ { "value": "8px", "rem": "0.5rem", "count": 1 }
78
+ ],
79
+ "shadows": []
80
+ },
81
+ "tree": {
82
+ "name": "Button",
83
+ "type": "COMPONENT",
84
+ "width": { "px": 120, "rem": 7.5 },
85
+ "height": { "px": 40, "rem": 2.5 },
86
+ "layout": {
87
+ "direction": "row",
88
+ "gap": { "px": 8, "rem": 0.5 },
89
+ "padding": { "top": { "px": 8 }, "right": { "px": 16 }, "bottom": { "px": 8 }, "left": { "px": 16 } },
90
+ "mainAxisAlign": "CENTER",
91
+ "crossAxisAlign": "CENTER"
92
+ },
93
+ "fills": [{ "type": "SOLID", "hex": "#6366f1" }],
94
+ "borderRadius": { "px": 8, "rem": 0.5 },
95
+ "children": [
96
+ {
97
+ "name": "Label",
98
+ "type": "TEXT",
99
+ "text": {
100
+ "content": "Click me",
101
+ "fontFamily": "Inter",
102
+ "fontStyle": "Medium",
103
+ "fontSize": { "px": 14, "rem": 0.875 },
104
+ "textAlign": "center"
105
+ },
106
+ "fills": [{ "type": "SOLID", "hex": "#ffffff" }]
107
+ }
108
+ ]
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## When to use `document` vs other commands
114
+
115
+ | Need | Command |
116
+ |------|---------|
117
+ | Quick look at one element | `fig inspect` |
118
+ | Full component with all children | `fig document` |
119
+ | Just CSS for one element | `fig css` |
120
+ | All unique styles in a frame | `fig styles` |
121
+ | Spacing between two elements | `fig measure` |
122
+
123
+ ## Workflow: Design to Code
124
+
125
+ 1. Select the component in Figma
126
+ 2. Run `fig document --json` to get the full spec
127
+ 3. Use the JSON tree to build the component — each node maps to an element, children map to nested elements
128
+ 4. Use the `tokens` section to set up CSS variables or Tailwind config
129
+ 5. Verify with `fig verify`
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: figma-inspect
3
+ description: |
4
+ Use this skill when the user wants detailed design specifications from Figma — dimensions, spacing, padding, gap, colors, typography, shadows, border radius, opacity, or variable bindings. Triggers on: "inspect", "get specs", "what are the dimensions", "what font is this", "what color", "spacing", "padding", "design specs", "pixel values", "check the measurements". Also use when translating a Figma design to code and needing exact values. Requires an element to be selected in Figma or a node ID / Figma link.
5
+ allowed-tools:
6
+ - Bash(fig inspect *)
7
+ - Bash(fig inspect)
8
+ - Bash(fig measure *)
9
+ - Bash(fig measure)
10
+ ---
11
+
12
+ # Figma Inspect
13
+
14
+ Get detailed design specs for any element in Figma. All values are returned in both **px and rem** (base 16px).
15
+
16
+ ## Prerequisites
17
+
18
+ The `fig` CLI must be connected. Check with `fig daemon status`. If not connected: `fig connect --safe`.
19
+
20
+ ## Usage
21
+
22
+ ### Inspect current selection
23
+
24
+ The user must select an element in Figma first, then:
25
+
26
+ ```bash
27
+ fig inspect
28
+ ```
29
+
30
+ **Returns:**
31
+ - **Dimensions** — width, height (px + rem)
32
+ - **Position** — x, y (px + rem)
33
+ - **Layout** — flex direction, gap, padding (all 4 sides), main/cross axis alignment, wrap
34
+ - **Border radius** — uniform or per-corner (px + rem)
35
+ - **Fills** — hex color, rgba, opacity, gradient stops
36
+ - **Strokes** — color, weight, alignment
37
+ - **Typography** — font family, style, size, weight, line-height, letter-spacing, text-align, decoration, transform
38
+ - **Effects** — drop shadow, inner shadow, blur (offset, radius, spread, color)
39
+ - **Opacity** — when < 1
40
+ - **Variable bindings** — which design tokens are bound to which properties
41
+ - **Component info** — name, set, main vs instance
42
+
43
+ ### Inspect from a Figma link
44
+
45
+ ```bash
46
+ fig inspect --link "https://www.figma.com/design/FILEID/Name?node-id=123-456"
47
+ ```
48
+
49
+ ### Inspect a specific node
50
+
51
+ ```bash
52
+ fig inspect --node "123:456"
53
+ ```
54
+
55
+ ### Inspect element + all children
56
+
57
+ ```bash
58
+ fig inspect --deep
59
+ ```
60
+
61
+ This inspects the selected element and then each of its direct children, giving you full specs for a complete component (e.g., a card with its title, description, button).
62
+
63
+ ### Raw JSON output
64
+
65
+ ```bash
66
+ fig inspect --json
67
+ ```
68
+
69
+ Use `--json` when you need to programmatically process the specs or pass them to another tool.
70
+
71
+ ## Measuring spacing between elements
72
+
73
+ Select exactly **2 elements** in Figma, then:
74
+
75
+ ```bash
76
+ fig measure
77
+ ```
78
+
79
+ Returns:
80
+ - Horizontal gap between the two elements (px + rem)
81
+ - Vertical gap between the two elements (px + rem)
82
+ - Center-to-center distance on both axes
83
+
84
+ ## Workflow: From Specs to Code
85
+
86
+ 1. User says "make this button match the Figma design"
87
+ 2. Ask user to select the button in Figma
88
+ 3. Run `fig inspect` to get exact specs
89
+ 4. Run `fig inspect --deep` if it has children (label, icon, etc.)
90
+ 5. Apply dimensions, padding, colors, typography, border-radius to code
91
+ 6. Run `fig css` if you want ready-to-paste CSS
92
+
93
+ ## Tips
94
+
95
+ - If inspect returns nothing for fills/colors, the element itself may have no fill — check its children with `--deep`
96
+ - For spacing between sibling elements, use `fig measure` instead of calculating manually
97
+ - Use `fig inspect --json | jq '.specs[0].typography'` to extract just typography
98
+ - Variable bindings tell you which design tokens are used — map these to your CSS variables or Tailwind config