framer-dalton 0.0.2 → 0.0.5
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/README.md +16 -6
- package/dist/cli.js +14035 -6321
- package/dist/start-relay-server.js +158 -165
- package/docs/all-skills.md +6 -0
- package/docs/code-components.md +115 -0
- package/docs/component-examples.md +869 -0
- package/docs/property-controls.md +1535 -0
- package/docs/server-api.md +755 -0
- package/package.json +7 -3
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
## CLI Usage
|
|
2
|
+
|
|
3
|
+
### Required Workflow
|
|
4
|
+
|
|
5
|
+
Every task follows these steps:
|
|
6
|
+
|
|
7
|
+
#### 1. Connect (once per session)
|
|
8
|
+
|
|
9
|
+
Ask for the Project URL, then create a session:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx framer-dalton session new "<projectUrl>" # Uses cached API key
|
|
13
|
+
npx framer-dalton session new "<projectUrl>" "<apiKey>" # First time: needs API key (Project Settings > General > API Keys)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
#### 2. Look up the API (before EVERY code execution)
|
|
17
|
+
|
|
18
|
+
**You MUST run `npx framer-dalton docs` before writing any code.** Do not guess method names or signatures.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx framer-dalton docs Collection # What methods exist?
|
|
22
|
+
npx framer-dalton docs Collection.getItems # What are the parameters and return type?
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
#### 3. Execute code
|
|
26
|
+
|
|
27
|
+
Only after checking docs:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx framer-dalton -s 1 -e "state.items = await collection.getItems(); console.log(state.items.length)"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
#### 4. Store results in `state`
|
|
34
|
+
|
|
35
|
+
Always save results you'll need again. Don't repeat API calls.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
**Do not skip step 2.** The examples below are patterns only - always verify current signatures with `npx framer-dalton docs`.
|
|
40
|
+
|
|
41
|
+
### Communication style
|
|
42
|
+
|
|
43
|
+
Be concise. Don't narrate implementation details like field IDs, escaping, or internal steps. Just do the work and report what was accomplished in user-facing terms.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### Session Management
|
|
48
|
+
|
|
49
|
+
Each session maintains a persistent connection to a Framer project. Use sessions to:
|
|
50
|
+
|
|
51
|
+
- Keep state separate between different tasks
|
|
52
|
+
- Persist data across multiple execute calls
|
|
53
|
+
- Reuse the `framer` API instance without reconnecting
|
|
54
|
+
|
|
55
|
+
Get a new session ID:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx framer-dalton session new "https://framer.com/projects/Website--abc123" "framer_api_xxx"
|
|
59
|
+
# outputs: 1
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Always use your own session** - pass `-s <id>` to all commands. Using the same session preserves your `state` between calls.
|
|
63
|
+
|
|
64
|
+
List active sessions:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx framer-dalton session list
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Execute Code
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx framer-dalton -s <sessionId> -e "<code>"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Escaping:** For code with HTML, quotes, or special characters, use a heredoc (see below). For simple strings, use `$'...'` syntax.
|
|
77
|
+
|
|
78
|
+
**Examples:**
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Fetch collections and store in state (always store results you'll reuse)
|
|
82
|
+
npx framer-dalton -s 1 -e "state.collections = await framer.getCollections(); console.log(state.collections.map(c => c.name))"
|
|
83
|
+
|
|
84
|
+
# Use stored data in subsequent calls
|
|
85
|
+
npx framer-dalton -s 1 -e "state.team = state.collections.find(c => c.name === 'Team')"
|
|
86
|
+
npx framer-dalton -s 1 -e "state.teamItems = await state.team.getItems(); console.log(state.teamItems.length)"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Multiline code with heredoc (recommended for complex strings):**
|
|
90
|
+
|
|
91
|
+
For code containing HTML, quotes, or special characters, use a heredoc to avoid escaping issues:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npx framer-dalton -s 1 <<'EOF'
|
|
95
|
+
const translations = {
|
|
96
|
+
"node-id": "<h2>Ship's Treasures</h2>",
|
|
97
|
+
"other-id": "<p>Text with "quotes" and <tags></p>"
|
|
98
|
+
};
|
|
99
|
+
await framer.setLocalizationData({ valuesBySource: translations });
|
|
100
|
+
EOF
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The `<<'EOF'` syntax (with quotes around EOF) prevents shell interpolation.
|
|
104
|
+
|
|
105
|
+
**Alternative: pipe from file:**
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cat script.js | npx framer-dalton -s 1
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Simple inline code:**
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx framer-dalton -s 1 -e $'
|
|
115
|
+
const collections = await framer.getCollections();
|
|
116
|
+
for (const c of collections) {
|
|
117
|
+
const items = await c.getItems();
|
|
118
|
+
console.log(c.name, items.length);
|
|
119
|
+
}
|
|
120
|
+
'
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### API Documentation
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx framer-dalton docs # List all available methods
|
|
127
|
+
npx framer-dalton docs Collection # Show class with all method signatures
|
|
128
|
+
npx framer-dalton docs Collection.addItems # Show method + recursively expand all referenced types
|
|
129
|
+
npx framer-dalton docs ScreenshotOptions # Show type + recursively expand all referenced types
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`docs` with no arguments lists available methods. Looking up a class shows its full definition without expanding referenced types. Looking up a specific method or type automatically expands all referenced types recursively.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Context Variables
|
|
137
|
+
|
|
138
|
+
- `framer` - Connected Framer Server API instance
|
|
139
|
+
- `state` - Object persisted between calls within your session
|
|
140
|
+
- `console` - For output (`console.log`, `console.error`)
|
|
141
|
+
- `require` - Sandboxed Node.js modules: fs, path, url, crypto, buffer, util, os
|
|
142
|
+
- Standard globals: `fetch`, `Buffer`, `URL`, `crypto`, `setTimeout`
|
|
143
|
+
|
|
144
|
+
**Note:** `fs` operations are sandboxed to cwd, /tmp, and os.tmpdir().
|
|
145
|
+
|
|
146
|
+
### Use `state` to Avoid Repeated Calls
|
|
147
|
+
|
|
148
|
+
**Always store results in `state` when you'll need them again.** API calls are slow - don't repeat them.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# First call: fetch and store
|
|
152
|
+
npx framer-dalton -s 1 -e "state.collections = await framer.getCollections()"
|
|
153
|
+
|
|
154
|
+
# Later calls: reuse from state
|
|
155
|
+
npx framer-dalton -s 1 -e "const team = state.collections.find(c => c.name === 'Team')"
|
|
156
|
+
npx framer-dalton -s 1 -e "state.teamItems = await state.collections.find(c => c.name === 'Team').getItems()"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Store anything you'll reference again: collections, items, nodes, fields.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## API Examples
|
|
164
|
+
|
|
165
|
+
**STOP: These are patterns only. Before using any method below, run `npx framer-dalton docs <ClassName>` to verify the current signature.**
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Working with Collections (CMS)
|
|
170
|
+
|
|
171
|
+
Collections are Framer's CMS. Each collection has fields (columns) and items (rows).
|
|
172
|
+
|
|
173
|
+
### Reading Collections
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
// Get all collections
|
|
177
|
+
const collections = await framer.getCollections();
|
|
178
|
+
console.log(collections.map((c) => ({ name: c.name, id: c.id })));
|
|
179
|
+
|
|
180
|
+
// Get a specific collection by ID
|
|
181
|
+
const collection = await framer.getCollection("collection-id");
|
|
182
|
+
|
|
183
|
+
// Get fields (columns) - returns array of { id, type, name }
|
|
184
|
+
const fields = await collection.getFields();
|
|
185
|
+
console.log(fields);
|
|
186
|
+
// [{ id: "BnNuS2i3o", type: "string", name: "Title" }, ...]
|
|
187
|
+
|
|
188
|
+
// Get items (rows)
|
|
189
|
+
const items = await collection.getItems();
|
|
190
|
+
console.log(items);
|
|
191
|
+
// [{ id: "XTM8FSHGs", slug: "post-1", draft: false, fieldData: {...} }, ...]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Field Types
|
|
195
|
+
|
|
196
|
+
`boolean`, `color`, `number`, `string`, `formattedText` (HTML), `image`, `file`, `link`, `date`, `enum`, `collectionReference`, `multiCollectionReference`, `array` (galleries)
|
|
197
|
+
|
|
198
|
+
### Updating Collection Items
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
// Add or update items (if id matches existing item, it updates)
|
|
202
|
+
await collection.addItems([
|
|
203
|
+
{
|
|
204
|
+
id: "new-item-1",
|
|
205
|
+
slug: "hello-world",
|
|
206
|
+
fieldData: { titleFieldId: "Hello World" },
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
// Remove items
|
|
211
|
+
await collection.removeItems(["item-id-1", "item-id-2"]);
|
|
212
|
+
|
|
213
|
+
// Reorder items
|
|
214
|
+
const ids = items.map((i) => i.id).reverse();
|
|
215
|
+
await collection.setItemOrder(ids);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Working with CollectionItem
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
const items = await collection.getItems();
|
|
222
|
+
const item = items[0];
|
|
223
|
+
|
|
224
|
+
// Update item attributes
|
|
225
|
+
await item.setAttributes({ slug: "new-slug" });
|
|
226
|
+
|
|
227
|
+
// Navigate to item in Framer UI
|
|
228
|
+
await item.navigateTo();
|
|
229
|
+
|
|
230
|
+
// Remove item
|
|
231
|
+
await item.remove();
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Managed Collections
|
|
235
|
+
|
|
236
|
+
Managed collections are fully controlled by code - users can't edit them directly. Use for syncing external data sources.
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
// Create a managed collection
|
|
240
|
+
const managed = await framer.createManagedCollection({ name: "My Sync" });
|
|
241
|
+
|
|
242
|
+
// Set fields
|
|
243
|
+
await managed.setFields([
|
|
244
|
+
{ id: "title", name: "Title", type: "string" },
|
|
245
|
+
{ id: "content", name: "Content", type: "formattedText" },
|
|
246
|
+
{ id: "published", name: "Published", type: "date" },
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
// Add items
|
|
250
|
+
await managed.addItems([
|
|
251
|
+
{
|
|
252
|
+
id: "1",
|
|
253
|
+
slug: "first-post",
|
|
254
|
+
fieldData: { title: "Hello", content: "<p>World</p>" },
|
|
255
|
+
},
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
// Store sync metadata
|
|
259
|
+
await managed.setPluginData("lastSync", new Date().toISOString());
|
|
260
|
+
const lastSync = await managed.getPluginData("lastSync");
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Working with Nodes
|
|
266
|
+
|
|
267
|
+
Nodes are layers on the canvas: frames, text, images, components, etc.
|
|
268
|
+
|
|
269
|
+
### Getting Nodes
|
|
270
|
+
|
|
271
|
+
```js
|
|
272
|
+
// Get current selection
|
|
273
|
+
const selection = await framer.getSelection();
|
|
274
|
+
|
|
275
|
+
// Get canvas root
|
|
276
|
+
const root = await framer.getCanvasRoot();
|
|
277
|
+
|
|
278
|
+
// Get node by ID
|
|
279
|
+
const node = await framer.getNode("node-id");
|
|
280
|
+
|
|
281
|
+
// Get children of a node
|
|
282
|
+
const children = await framer.getChildren(node.id);
|
|
283
|
+
|
|
284
|
+
// Get parent
|
|
285
|
+
const parent = await framer.getParent(node.id);
|
|
286
|
+
|
|
287
|
+
// Find nodes by type
|
|
288
|
+
const frameNodes = await framer.getNodesWithType("FrameNode");
|
|
289
|
+
const textNodes = await framer.getNodesWithType("TextNode");
|
|
290
|
+
|
|
291
|
+
// Find nodes with specific attribute set
|
|
292
|
+
const nodesWithBg = await framer.getNodesWithAttributeSet("backgroundColor");
|
|
293
|
+
const nodesWithImage = await framer.getNodesWithAttributeSet("backgroundImage");
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Creating Nodes
|
|
297
|
+
|
|
298
|
+
```js
|
|
299
|
+
// Create a frame
|
|
300
|
+
const frame = await framer.createFrameNode({
|
|
301
|
+
name: "My Frame",
|
|
302
|
+
width: 200,
|
|
303
|
+
height: 100,
|
|
304
|
+
backgroundColor: "rgba(255, 0, 0, 1)",
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Create text (creates TextNode, then use setText)
|
|
308
|
+
const textNode = await framer.createTextNode({ name: "My Text" });
|
|
309
|
+
await textNode.setText("Hello World");
|
|
310
|
+
|
|
311
|
+
// Add to specific parent
|
|
312
|
+
const child = await framer.createFrameNode({ name: "Child" }, parentNode.id);
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Modifying Nodes
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
// Update attributes
|
|
319
|
+
await node.setAttributes({
|
|
320
|
+
name: "New Name",
|
|
321
|
+
width: 300,
|
|
322
|
+
height: 200,
|
|
323
|
+
backgroundColor: "rgba(0, 0, 255, 0.5)",
|
|
324
|
+
visible: true,
|
|
325
|
+
locked: false,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Move to different parent
|
|
329
|
+
await framer.setParent(node.id, newParentId);
|
|
330
|
+
|
|
331
|
+
// Clone a node
|
|
332
|
+
const clone = await node.clone();
|
|
333
|
+
|
|
334
|
+
// Remove nodes
|
|
335
|
+
await framer.removeNodes([node.id]);
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Working with Text
|
|
339
|
+
|
|
340
|
+
```js
|
|
341
|
+
// Get text content
|
|
342
|
+
const text = await framer.getText(); // from selection
|
|
343
|
+
const nodeText = await textNode.getText();
|
|
344
|
+
|
|
345
|
+
// Set text
|
|
346
|
+
await framer.setText("New text"); // on selection
|
|
347
|
+
await textNode.setText("Hello World");
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Working with Images
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
// Add image to canvas
|
|
356
|
+
await framer.addImage({
|
|
357
|
+
image: "https://example.com/image.png",
|
|
358
|
+
name: "My Image",
|
|
359
|
+
altText: "Description",
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// Set image on selected node
|
|
363
|
+
await framer.setImage({
|
|
364
|
+
image: "https://example.com/image.png",
|
|
365
|
+
altText: "Description",
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Upload image without adding to canvas (for later use)
|
|
369
|
+
const imageAsset = await framer.uploadImage({
|
|
370
|
+
image: "https://example.com/image.png",
|
|
371
|
+
name: "My Image",
|
|
372
|
+
altText: "Alt text",
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Use uploaded image when creating a frame
|
|
376
|
+
await framer.createFrameNode({ backgroundImage: imageAsset });
|
|
377
|
+
|
|
378
|
+
// Get image from selection
|
|
379
|
+
const image = await framer.getImage();
|
|
380
|
+
console.log(image?.url);
|
|
381
|
+
|
|
382
|
+
// Add SVG (must be < 10kb)
|
|
383
|
+
await framer.addSVG({
|
|
384
|
+
svg: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="10" cy="10" r="8"/></svg>',
|
|
385
|
+
name: "circle.svg",
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Working with Styles
|
|
392
|
+
|
|
393
|
+
### Color Styles
|
|
394
|
+
|
|
395
|
+
```js
|
|
396
|
+
// List all color styles
|
|
397
|
+
const colorStyles = await framer.getColorStyles();
|
|
398
|
+
|
|
399
|
+
// Get specific style
|
|
400
|
+
const style = await framer.getColorStyle("style-id");
|
|
401
|
+
|
|
402
|
+
// Create color style (supports light/dark themes)
|
|
403
|
+
const newStyle = await framer.createColorStyle({
|
|
404
|
+
name: "Brand/Primary", // Use / for folders
|
|
405
|
+
light: "rgba(242, 59, 57, 1)",
|
|
406
|
+
dark: "rgba(180, 40, 40, 1)",
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Update style
|
|
410
|
+
await style.setAttributes({ light: "rgba(0, 100, 200, 1)" });
|
|
411
|
+
|
|
412
|
+
// Remove style
|
|
413
|
+
await style.remove();
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### Text Styles
|
|
417
|
+
|
|
418
|
+
```js
|
|
419
|
+
// List all text styles
|
|
420
|
+
const textStyles = await framer.getTextStyles();
|
|
421
|
+
|
|
422
|
+
// Create text style
|
|
423
|
+
const heading = await framer.createTextStyle({
|
|
424
|
+
name: "Typography/Heading 1",
|
|
425
|
+
tag: "h1",
|
|
426
|
+
fontSize: "48px",
|
|
427
|
+
lineHeight: "1.2em",
|
|
428
|
+
fontWeight: 700,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// With breakpoints for responsive typography
|
|
432
|
+
const responsive = await framer.createTextStyle({
|
|
433
|
+
fontSize: "24px",
|
|
434
|
+
minWidth: 1280,
|
|
435
|
+
breakpoints: [
|
|
436
|
+
{ minWidth: 1024, fontSize: "20px" },
|
|
437
|
+
{ minWidth: 768, fontSize: "18px" },
|
|
438
|
+
{ minWidth: 320, fontSize: "16px" },
|
|
439
|
+
],
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// Update text style
|
|
443
|
+
await heading.setAttributes({ fontSize: "52px" });
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Fonts
|
|
447
|
+
|
|
448
|
+
```js
|
|
449
|
+
// Get available fonts
|
|
450
|
+
const fonts = await framer.getFonts();
|
|
451
|
+
|
|
452
|
+
// Get specific font
|
|
453
|
+
const font = await framer.getFont("Inter", { weight: 400 });
|
|
454
|
+
const boldFont = await framer.getFont("Inter", { weight: 700 });
|
|
455
|
+
|
|
456
|
+
// Use font in text style
|
|
457
|
+
await framer.createTextStyle({
|
|
458
|
+
name: "Body",
|
|
459
|
+
font,
|
|
460
|
+
boldFont,
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## Code Components
|
|
467
|
+
|
|
468
|
+
Code components are custom React components that run inside Framer. Use them when you need behavior that Framer's visual tools don't support:
|
|
469
|
+
|
|
470
|
+
- **Custom interactivity** — drag-to-reorder, gesture-driven animations, games, form validation
|
|
471
|
+
- **External data** — fetching from APIs, rendering dynamic content, real-time updates
|
|
472
|
+
- **Complex logic** — state machines, calculations, conditional rendering beyond simple variants
|
|
473
|
+
- **Third-party libraries** — maps, charts, video players, rich text editors
|
|
474
|
+
- **Canvas/WebGL** — custom drawing, 3D rendering, generative art
|
|
475
|
+
|
|
476
|
+
If the design can be built with Framer's built-in components, layout tools, and interactions — don't use a code component. Code components are harder to maintain and can't be visually edited.
|
|
477
|
+
|
|
478
|
+
### Workflow
|
|
479
|
+
|
|
480
|
+
1. **Create** a code file: `framer.createCodeFile("MyComponent.tsx", code)`
|
|
481
|
+
2. **Edit** an existing code file: `codeFile.setFileContent(newCode)`
|
|
482
|
+
3. **Type check**: `codeFile.typecheck()` or `framer.typecheckCode("File.tsx", code)`
|
|
483
|
+
4. **Add to canvas**: `framer.addComponentInstance({ url: codeFile.exports[0].insertURL })`
|
|
484
|
+
|
|
485
|
+
Use `npx framer-dalton docs CodeFile` and `npx framer-dalton docs Framer.createCodeFile` to look up the full API.
|
|
486
|
+
|
|
487
|
+
### Authoring guidance
|
|
488
|
+
|
|
489
|
+
Before writing component code, read the relevant skill docs:
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
npx framer-dalton skill code-components # Structure, constraints, layout annotations
|
|
493
|
+
npx framer-dalton skill property-controls # All 20+ control types with examples
|
|
494
|
+
npx framer-dalton skill component-examples # Complete reference components
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Storing Data
|
|
500
|
+
|
|
501
|
+
Store metadata on nodes or globally in the project.
|
|
502
|
+
|
|
503
|
+
```js
|
|
504
|
+
// Store global project data
|
|
505
|
+
await framer.setPluginData("myKey", "myValue");
|
|
506
|
+
const value = await framer.getPluginData("myKey");
|
|
507
|
+
|
|
508
|
+
// Store data on a node
|
|
509
|
+
await node.setPluginData("processed", "true");
|
|
510
|
+
const nodeData = await node.getPluginData("processed");
|
|
511
|
+
|
|
512
|
+
// List all keys
|
|
513
|
+
const keys = await framer.getPluginDataKeys();
|
|
514
|
+
const nodeKeys = await node.getPluginDataKeys();
|
|
515
|
+
|
|
516
|
+
// Delete data (set to null)
|
|
517
|
+
await framer.setPluginData("myKey", null);
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## Localization
|
|
523
|
+
|
|
524
|
+
```js
|
|
525
|
+
// Get all locales
|
|
526
|
+
const locales = await framer.getLocales();
|
|
527
|
+
const defaultLocale = await framer.getDefaultLocale();
|
|
528
|
+
|
|
529
|
+
// Get localization groups (pages, CMS items with translations)
|
|
530
|
+
const groups = await framer.getLocalizationGroups();
|
|
531
|
+
|
|
532
|
+
// Update translations
|
|
533
|
+
const french = locales.find((l) => l.code === "fr");
|
|
534
|
+
await framer.setLocalizationData({
|
|
535
|
+
valuesBySource: {
|
|
536
|
+
[sourceId]: {
|
|
537
|
+
[french.id]: { action: "set", value: "Bonjour" },
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
});
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Common Patterns
|
|
546
|
+
|
|
547
|
+
### Iterate over all nodes in project
|
|
548
|
+
|
|
549
|
+
```js
|
|
550
|
+
const root = await framer.getCanvasRoot();
|
|
551
|
+
for await (const node of root.walk()) {
|
|
552
|
+
console.log(node.name);
|
|
553
|
+
}
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
### Sync external data to collection
|
|
557
|
+
|
|
558
|
+
```js
|
|
559
|
+
const collection = await framer.getManagedCollection();
|
|
560
|
+
const existingIds = new Set(await collection.getItemIds());
|
|
561
|
+
|
|
562
|
+
const externalData = await fetch("https://api.example.com/posts").then((r) =>
|
|
563
|
+
r.json(),
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
const items = externalData.map((post) => ({
|
|
567
|
+
id: post.id,
|
|
568
|
+
slug: post.slug,
|
|
569
|
+
fieldData: { title: post.title, content: post.body },
|
|
570
|
+
}));
|
|
571
|
+
|
|
572
|
+
await collection.addItems(items);
|
|
573
|
+
|
|
574
|
+
// Remove items no longer in external source
|
|
575
|
+
const newIds = new Set(items.map((i) => i.id));
|
|
576
|
+
const toRemove = [...existingIds].filter((id) => !newIds.has(id));
|
|
577
|
+
if (toRemove.length) await collection.removeItems(toRemove);
|
|
578
|
+
|
|
579
|
+
await collection.setPluginData("lastSync", new Date().toISOString());
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
### Batch update all images' alt text
|
|
583
|
+
|
|
584
|
+
```js
|
|
585
|
+
const nodes = await framer.getNodesWithAttributeSet("backgroundImage");
|
|
586
|
+
for (const node of nodes) {
|
|
587
|
+
if (!node.backgroundImage) continue;
|
|
588
|
+
await node.setAttributes({
|
|
589
|
+
backgroundImage: node.backgroundImage.cloneWithAttributes({
|
|
590
|
+
altText: "Updated description",
|
|
591
|
+
}),
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
## Screenshots and SVG Export
|
|
599
|
+
|
|
600
|
+
Server API exclusive methods for capturing visual output from nodes.
|
|
601
|
+
|
|
602
|
+
```js
|
|
603
|
+
// Take a screenshot of a node (returns Buffer + mimeType)
|
|
604
|
+
const result = await framer.screenshot(node.id);
|
|
605
|
+
console.log(result.mimeType); // "image/png"
|
|
606
|
+
const os = require("os");
|
|
607
|
+
await require("fs").promises.writeFile(
|
|
608
|
+
os.tmpdir() + "/screenshot.png",
|
|
609
|
+
result.data,
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
// With options
|
|
613
|
+
const jpg = await framer.screenshot(node.id, {
|
|
614
|
+
format: "jpeg", // "png" (default) or "jpeg"
|
|
615
|
+
quality: 90, // JPEG quality 0-100 (default 100)
|
|
616
|
+
scale: 2, // Pixel density: 0.5, 1, 1.5, 2, 3, or 4 (default 1)
|
|
617
|
+
clip: { x: 0, y: 0, width: 100, height: 100 }, // Clip region in CSS pixels
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// Export as SVG string
|
|
621
|
+
const svgString = await framer.exportSVG(node.id);
|
|
622
|
+
await require("fs").promises.writeFile(os.tmpdir() + "/export.svg", svgString);
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
## Publishing and Deployments
|
|
628
|
+
|
|
629
|
+
Server API exclusive methods for publishing and managing deployments.
|
|
630
|
+
|
|
631
|
+
```js
|
|
632
|
+
// Publish the project (creates a new deployment)
|
|
633
|
+
const result = await framer.publish();
|
|
634
|
+
console.log(result.deployment.id); // Deployment ID
|
|
635
|
+
console.log(result.hostnames); // Array of hostnames
|
|
636
|
+
|
|
637
|
+
// Get all deployments
|
|
638
|
+
const deployments = await framer.getDeployments();
|
|
639
|
+
for (const d of deployments) {
|
|
640
|
+
console.log(d.id, d.createdAt);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Deploy a specific deployment to domains
|
|
644
|
+
const hostnames = await framer.deploy(deploymentId, ["example.com"]);
|
|
645
|
+
|
|
646
|
+
// Get current publish info (production and staging URLs)
|
|
647
|
+
const info = await framer.getPublishInfo();
|
|
648
|
+
console.log(info.production?.url); // Production URL
|
|
649
|
+
console.log(info.staging?.url); // Staging URL
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## Change Tracking
|
|
655
|
+
|
|
656
|
+
Server API exclusive methods for tracking project changes.
|
|
657
|
+
|
|
658
|
+
```js
|
|
659
|
+
// Get changed paths since last publish
|
|
660
|
+
const changes = await framer.getChangedPaths();
|
|
661
|
+
console.log(changes.added); // New paths
|
|
662
|
+
console.log(changes.modified); // Modified paths
|
|
663
|
+
console.log(changes.removed); // Removed paths
|
|
664
|
+
|
|
665
|
+
// Get contributors to changes (returns user IDs)
|
|
666
|
+
const contributors = await framer.getChangeContributors();
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
## Canvas Editing
|
|
672
|
+
|
|
673
|
+
**Use this for all design tasks** — creating pages, building sections, recreating designs from screenshots, or any task that involves layout or visual styling on the canvas.
|
|
674
|
+
|
|
675
|
+
**Hard rule:** For design/layout work, do **not** use low-level node APIs (`createNode`, `setAttributes`, `setRect`, etc.). Always use the canvas editing flow (`getAgentSystemPrompt` + `getAgentContext` + `readProjectForAgent` + `applyAgentChanges`). If you start writing low-level node code for design work, stop and switch to canvas editing methods.
|
|
676
|
+
|
|
677
|
+
**Access note:** Canvas editing `agent` methods are employee-only. If these methods fail or are unavailable, the account likely does not have employee access.
|
|
678
|
+
|
|
679
|
+
### Methods
|
|
680
|
+
|
|
681
|
+
**`framer.getAgentSystemPrompt()`** — Returns the command reference, design rules, layout patterns, and examples. This is the sole documentation for the command syntax and query types. It is static (same for every project) — fetch once per session and persist to a file.
|
|
682
|
+
|
|
683
|
+
**`framer.getAgentContext({ pagePath })`** — Returns the project's available fonts, components, color tokens, text style presets, and icon sets for the target page scope. Fetch before generating commands and use the same `pagePath` across context/read/apply calls.
|
|
684
|
+
|
|
685
|
+
**`framer.readProjectForAgent(queries, { pagePath })`** — Reads project state. Query types are documented in the system prompt. Use liberally — query the page tree, icon sets, components, and examples before generating commands. Multiple queries can be batched in a single call. Never guess at names for examples, icon sets, or fonts — always look them up first.
|
|
686
|
+
|
|
687
|
+
**`framer.applyAgentChanges(dsl, { pagePath })`** — Applies commands to the canvas. Node IDs you assign are temporary — re-read the page tree via `readProjectForAgent` if you need actual IDs after applying.
|
|
688
|
+
|
|
689
|
+
### Workflow
|
|
690
|
+
|
|
691
|
+
```js
|
|
692
|
+
// 1. Save the system prompt to a file (once per session).
|
|
693
|
+
const fs = require("fs");
|
|
694
|
+
const prompt = await framer.getAgentSystemPrompt();
|
|
695
|
+
fs.writeFileSync("/tmp/framer-canvas-reference.txt", prompt);
|
|
696
|
+
console.log("Saved to /tmp/framer-canvas-reference.txt —", prompt.length, "chars");
|
|
697
|
+
|
|
698
|
+
// 2. Get project context
|
|
699
|
+
const projectCtx = await framer.getAgentContext({ pagePath: "/" });
|
|
700
|
+
console.log(projectCtx);
|
|
701
|
+
|
|
702
|
+
// 3. Read project state (query types are in the prompt)
|
|
703
|
+
const { results } = await framer.readProjectForAgent(
|
|
704
|
+
[{ type: "page", path: "/" }],
|
|
705
|
+
{ pagePath: "/" }
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
// 4. Apply changes
|
|
709
|
+
await framer.applyAgentChanges(dsl, { pagePath: "/" });
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
**You must read the entire system prompt file before writing any commands.** It contains command syntax, design rules, layout patterns, examples, and query documentation. Skipping sections will cause incorrect output.
|
|
713
|
+
|
|
714
|
+
After applying changes, take screenshots to inspect the result. Check padding, spacing, typography, icon choice, and alignment. Iterate with additional `readProjectForAgent` + `applyAgentChanges` passes until visually accurate.
|
|
715
|
+
|
|
716
|
+
When setting text content, use raw Unicode characters directly (e.g. `→` not `\u2192`).
|
|
717
|
+
|
|
718
|
+
Always fetch fresh context per session — the format may change between versions.
|
|
719
|
+
|
|
720
|
+
### What canvas editing can do
|
|
721
|
+
|
|
722
|
+
Beyond basic page layout, the command language supports:
|
|
723
|
+
|
|
724
|
+
- **Colour tokens** — Create color tokens (`ColorStyleTokenNode`) with light/dark mode variants. Reference them across the project for consistent theming.
|
|
725
|
+
- **Text style presets** — Create reusable typography presets (`TextStylePresetNode`) and apply them to text nodes. Build full typographic systems (headings, body, captions, etc.).
|
|
726
|
+
- **Smart components** — Author reusable components (`ComponentNode`) with multiple visual variants, scoped variables, and property controls. Instantiate them anywhere with `ComponentInstanceNode`.
|
|
727
|
+
- **Interactive effects** — Add hover effects (scale, opacity, color), tap effects, scroll-triggered appear animations, transitions between component variants, and event handlers.
|
|
728
|
+
- **Icon sets** — Insert icons from named vector sets. Query available sets and icons via `readProjectForAgent`.
|
|
729
|
+
- **Responsive breakpoints** — Create page breakpoints (Desktop, Tablet, Mobile) using the replica/variant system, with per-breakpoint overrides.
|
|
730
|
+
- **Complex layouts** — Stack and grid layouts with auto-fill, fractional units, space-between distribution, and nested grids for asymmetric layouts.
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
## Capabilities
|
|
735
|
+
|
|
736
|
+
What you can do with the Framer CLI:
|
|
737
|
+
|
|
738
|
+
- **Canvas Editing** For design tasks — creating or editing pages, sections, layouts, recreating designs from screenshots, etc.
|
|
739
|
+
- **CMS**: Create, read, update, delete collections and items. Sync external databases.
|
|
740
|
+
- **Styles**: Manage color and text styles. Sync design systems.
|
|
741
|
+
- **Code Components**: Create, edit, type-check, and add custom React components to the canvas.
|
|
742
|
+
- **Assets**: Upload and manage images and files.
|
|
743
|
+
- **Localization**: Manage translations programmatically.
|
|
744
|
+
- **Data**: Store metadata on nodes and projects for plugin state.
|
|
745
|
+
- **Screenshots**: Capture node screenshots as PNG/JPEG. Export nodes as SVG.
|
|
746
|
+
- **Publishing**: Publish projects, manage deployments, track changes.
|
|
747
|
+
- **Low-level Node APIs**: Create and modify individual nodes. Only use these for targeted, surgical edits to specific nodes — not for building pages or layouts.
|
|
748
|
+
|
|
749
|
+
---
|
|
750
|
+
|
|
751
|
+
## Known Limitations
|
|
752
|
+
|
|
753
|
+
- **Pages**: No list, delete, update, move, or settings APIs (create only)
|
|
754
|
+
- **Code overrides**: Cannot assign overrides to nodes
|
|
755
|
+
- **Analytics**: No APIs exist for accessing analytics data
|