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.
@@ -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