framer-dalton 0.0.21 → 0.0.22

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.
@@ -15,7 +15,7 @@ npx framer-dalton@latest setup
15
15
 
16
16
  What you can do with the Framer CLI:
17
17
 
18
- - **Canvas Editing** For design tasks - creating or editing pages, sections, layouts, recreating designs from screenshots, etc.
18
+ - **Canvas Editing**: For design tasks creating or editing pages, sections, layouts, recreating designs from screenshots, etc.
19
19
  - **CMS**: Create, read, update, delete collections and items. Sync external databases.
20
20
  - **Styles**: Manage color and text styles. Sync design systems.
21
21
  - **Code Components**: Create, edit, type-check, and add custom React components to the canvas.
@@ -24,7 +24,6 @@ What you can do with the Framer CLI:
24
24
  - **Data**: Store metadata on nodes and projects for plugin state.
25
25
  - **Screenshots**: Capture node screenshots as PNG/JPEG. Export nodes as SVG.
26
26
  - **Publishing**: Publish projects, manage deployments, track changes.
27
- - **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.
28
27
 
29
28
  ## CLI Usage
30
29
 
@@ -159,9 +158,9 @@ List active sessions:
159
158
  npx framer-dalton session list
160
159
  ```
161
160
 
162
- ## Canvas Editing
161
+ ## Project-scoped skill
163
162
 
164
- For design tasks, do not try to build or restyle pages with low-level node APIs. Instead, after session creation, load the dynamically created project-scoped skill `framer-canvas-editing-project-<projectId>` for canvas editing on the connected project. It contains the canvas editing guidance and all related documentation for how to use the relevant methods.
163
+ **Always load the project-scoped skill `framer-canvas-editing-project-<projectId>` immediately after `session new`, regardless of the task.** It is the source of truth for the connected project and is required for canvas editing, project-tree reads, change review, publishing, page management, sourcing stock images, and component operations. CMS plugin API, code components, localization, plugin data, and CLI mechanics remain in this framer-dalton skill — use both together.
165
164
 
166
165
  ## Canvas Editing: Alternative Approach (“Prompt the Framer agent”)
167
166
 
@@ -199,7 +198,7 @@ Prompting may take a while to complete, so set the command timeout to 10 minutes
199
198
 
200
199
  ## Execute Code
201
200
 
202
- Write your code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and execute with `-f`:
201
+ Use your write tool to write your code to a unique file under `{{FRAMER_TEMPORARY_DIR}}/` and execute with `-f`:
203
202
 
204
203
  ```bash
205
204
  npx framer-dalton exec -s <sessionId> -f {{FRAMER_TEMPORARY_DIR}}/<sessionId>-<short-summary>.js
@@ -223,10 +222,9 @@ npx framer-dalton docs ScreenshotOptions # Show type + recursively expa
223
222
 
224
223
  ### Working with Collections (CMS)
225
224
 
226
- Collections are Framer's CMS. Each collection has fields (columns) and items (rows). There are two types:
225
+ Collections are Framer's CMS. Each collection has fields (columns) and items (rows). Use the plugin Collection API (the methods below) for collection schema and item CRUD.
227
226
 
228
- - **Unmanaged** (`framer.createCollection()`): Users can freely edit these in the Framer UI. Always use this by default.
229
- - **Managed** (`framer.createManagedCollection()`): Can ONLY be edited via the API. They appear read-only in the Framer UI. Only use managed collections when explicitly asked or when creating a script for data synchronisation.
227
+ **Exception — CMS Collection Lists.** The in-canvas construct that *renders* a collection as a repeating list of cards (the "Collection List" / repeater) is a canvas concern. Build and edit those through canvas editing, not the plugin API. Use the plugin Collection API to read available collections and field schema first, then drive the list through the canvas-editing skill.
230
228
 
231
229
  #### Reading Collections
232
230
 
@@ -289,228 +287,49 @@ await item.navigateTo();
289
287
  await item.remove();
290
288
  ```
291
289
 
292
- #### Managed Collections
293
-
294
- ```js
295
- // Create a managed collection
296
- const managed = await framer.createManagedCollection({ name: "My Sync" });
297
-
298
- // Set fields
299
- await managed.setFields([
300
- { id: "title", name: "Title", type: "string" },
301
- { id: "content", name: "Content", type: "formattedText" },
302
- { id: "published", name: "Published", type: "date" },
303
- ]);
304
-
305
- // Add items
306
- await managed.addItems([
307
- {
308
- id: "1",
309
- slug: "first-post",
310
- fieldData: { title: "Hello", content: "<p>World</p>" },
311
- },
312
- ]);
313
-
314
- // Store sync metadata
315
- await managed.setPluginData("lastSync", new Date().toISOString());
316
- const lastSync = await managed.getPluginData("lastSync");
317
- ```
318
-
319
- ### Working with Nodes
320
-
321
- Nodes are layers on the canvas: frames, text, images, components, etc.
322
-
323
- #### Getting Nodes
324
-
325
- ```js
326
- // Get current selection
327
- const selection = await framer.getSelection();
328
-
329
- // Get canvas root
330
- const root = await framer.getCanvasRoot();
331
-
332
- // Get node by ID
333
- const node = await framer.getNode("node-id");
334
-
335
- // Get children of a node
336
- const children = await framer.getChildren(node.id);
290
+ ### Working with Images
337
291
 
338
- // Get parent
339
- const parent = await framer.getParent(node.id);
292
+ Canvas editing accepts image URLs directly when setting an image fill, so the typical task is to obtain a URL and pass it through to the canvas-editing skill.
340
293
 
341
- // Find nodes by type
342
- const frameNodes = await framer.getNodesWithType("FrameNode");
343
- const textNodes = await framer.getNodesWithType("TextNode");
294
+ There are three sources you'll typically pull URLs from:
344
295
 
345
- // Find nodes with specific attribute set
346
- const nodesWithBg = await framer.getNodesWithAttributeSet("backgroundColor");
347
- const nodesWithImage = await framer.getNodesWithAttributeSet("backgroundImage");
348
- ```
349
-
350
- #### Creating Nodes
296
+ **Stock photography.** Use `framer.queryImagesForAgent` to source candidates and stash the URL you want:
351
297
 
352
298
  ```js
353
- // Create a frame
354
- const frame = await framer.createFrameNode({
355
- name: "My Frame",
356
- width: 200,
357
- height: 100,
358
- backgroundColor: "rgba(255, 0, 0, 1)",
299
+ const { results } = await framer.queryImagesForAgent({
300
+ source: "unsplash",
301
+ query: "snow-capped mountains",
302
+ count: 4,
303
+ orientation: "landscape",
359
304
  });
360
-
361
- // Create text (creates TextNode, then use setText)
362
- const textNode = await framer.createTextNode({ name: "My Text" });
363
- await textNode.setText("Hello World");
364
-
365
- // Add to specific parent
366
- const child = await framer.createFrameNode({ name: "Child" }, parentNode.id);
305
+ state.heroUrl = results[0].url;
367
306
  ```
368
307
 
369
- #### Modifying Nodes
308
+ **An external URL the user provided.** Pass it through directly. If you'll reuse the same image across many edits, upload it once and stash the resulting asset URL to avoid re-resolving the source on every change:
370
309
 
371
310
  ```js
372
- // Update attributes
373
- await node.setAttributes({
374
- name: "New Name",
375
- width: 300,
376
- height: 200,
377
- backgroundColor: "rgba(0, 0, 255, 0.5)",
378
- visible: true,
379
- locked: false,
380
- });
381
-
382
- // Move to different parent
383
- await framer.setParent(node.id, newParentId);
384
-
385
- // Clone a node
386
- const clone = await node.clone();
387
-
388
- // Remove nodes
389
- await framer.removeNodes([node.id]);
311
+ state.heroUrl = (await framer.uploadImage({
312
+ image: "https://example.com/hero.png",
313
+ altText: "Mountain range at sunset",
314
+ })).url;
390
315
  ```
391
316
 
392
- #### Working with Text
317
+ **An image already on the canvas.** Read the node and reuse its existing image URL:
393
318
 
394
319
  ```js
395
- // Get text content
396
- const text = await framer.getText(); // from selection
397
- const nodeText = await textNode.getText();
398
-
399
- // Set text
400
- await framer.setText("New text"); // on selection
401
- await textNode.setText("Hello World");
320
+ const node = await framer.getNodeForAgent({ id: "<image-node-id>" });
321
+ state.heroUrl = node.attributes.fill;
402
322
  ```
403
323
 
404
- ### Working with Images
324
+ For inline SVGs, use the plugin method directly:
405
325
 
406
326
  ```js
407
- // Add image to canvas
408
- await framer.addImage({
409
- image: "https://example.com/image.png",
410
- name: "My Image",
411
- altText: "Description",
412
- });
413
-
414
- // Set image on selected node
415
- await framer.setImage({
416
- image: "https://example.com/image.png",
417
- altText: "Description",
418
- });
419
-
420
- // Upload image without adding to canvas (for later use)
421
- const imageAsset = await framer.uploadImage({
422
- image: "https://example.com/image.png",
423
- name: "My Image",
424
- altText: "Alt text",
425
- });
426
-
427
- // Use uploaded image when creating a frame
428
- await framer.createFrameNode({ backgroundImage: imageAsset });
429
-
430
- // Get image from selection
431
- const image = await framer.getImage();
432
- console.log(image?.url);
433
-
434
- // Add SVG (must be < 10kb)
435
327
  await framer.addSVG({
436
328
  svg: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="10" cy="10" r="8"/></svg>',
437
329
  name: "circle.svg",
438
330
  });
439
331
  ```
440
332
 
441
- ### Working with Styles
442
-
443
- #### Color Styles
444
-
445
- ```js
446
- // List all color styles
447
- const colorStyles = await framer.getColorStyles();
448
-
449
- // Get specific style
450
- const style = await framer.getColorStyle("style-id");
451
-
452
- // Create color style (supports light/dark themes)
453
- const newStyle = await framer.createColorStyle({
454
- name: "Brand/Primary", // Use / for folders
455
- light: "rgba(242, 59, 57, 1)",
456
- dark: "rgba(180, 40, 40, 1)",
457
- });
458
-
459
- // Update style
460
- await style.setAttributes({ light: "rgba(0, 100, 200, 1)" });
461
-
462
- // Remove style
463
- await style.remove();
464
- ```
465
-
466
- #### Text Styles
467
-
468
- ```js
469
- // List all text styles
470
- const textStyles = await framer.getTextStyles();
471
-
472
- // Create text style
473
- const heading = await framer.createTextStyle({
474
- name: "Typography/Heading 1",
475
- tag: "h1",
476
- fontSize: "48px",
477
- lineHeight: "1.2em",
478
- fontWeight: 700,
479
- });
480
-
481
- // With breakpoints for responsive typography
482
- const responsive = await framer.createTextStyle({
483
- fontSize: "24px",
484
- minWidth: 1280,
485
- breakpoints: [
486
- { minWidth: 1024, fontSize: "20px" },
487
- { minWidth: 768, fontSize: "18px" },
488
- { minWidth: 320, fontSize: "16px" },
489
- ],
490
- });
491
-
492
- // Update text style
493
- await heading.setAttributes({ fontSize: "52px" });
494
- ```
495
-
496
- #### Fonts
497
-
498
- ```js
499
- // Get available fonts
500
- const fonts = await framer.getFonts();
501
-
502
- // Get specific font
503
- const font = await framer.getFont("Inter", { weight: 400 });
504
- const boldFont = await framer.getFont("Inter", { weight: 700 });
505
-
506
- // Use font in text style
507
- await framer.createTextStyle({
508
- name: "Body",
509
- font,
510
- boldFont,
511
- });
512
- ```
513
-
514
333
  ### Code Components
515
334
 
516
335
  Code components are custom React components that run inside Framer. Use them when you need behavior that Framer's visual tools don't support:
@@ -592,8 +411,9 @@ for await (const node of root.walk()) {
592
411
  #### Sync external data to collection
593
412
 
594
413
  ```js
595
- const collection = await framer.getManagedCollection();
596
- const existingIds = new Set(await collection.getItemIds());
414
+ const collection = await framer.getCollection("collection-id");
415
+ const existing = await collection.getItems();
416
+ const existingIds = new Set(existing.map((i) => i.id));
597
417
 
598
418
  const externalData = await fetch("https://api.example.com/posts").then((r) =>
599
419
  r.json(),
@@ -615,87 +435,6 @@ if (toRemove.length) await collection.removeItems(toRemove);
615
435
  await collection.setPluginData("lastSync", new Date().toISOString());
616
436
  ```
617
437
 
618
- #### Batch update all images' alt text
619
-
620
- ```js
621
- const nodes = await framer.getNodesWithAttributeSet("backgroundImage");
622
- for (const node of nodes) {
623
- if (!node.backgroundImage) continue;
624
- await node.setAttributes({
625
- backgroundImage: node.backgroundImage.cloneWithAttributes({
626
- altText: "Updated description",
627
- }),
628
- });
629
- }
630
- ```
631
-
632
- ### Screenshots and SVG Export
633
-
634
- Server API exclusive methods for capturing visual output from nodes.
635
-
636
- ```js
637
- // Take a screenshot of a node (returns Buffer + mimeType)
638
- const result = await framer.screenshot(node.id);
639
- console.log(result.mimeType); // "image/png"
640
- const os = require("os");
641
- await require("fs").promises.writeFile(
642
- os.tmpdir() + "/screenshot.png",
643
- result.data,
644
- );
645
-
646
- // With options
647
- const jpg = await framer.screenshot(node.id, {
648
- format: "jpeg", // "png" (default) or "jpeg"
649
- quality: 90, // JPEG quality 0-100 (default 100)
650
- scale: 2, // Pixel density: 0.5, 1, 1.5, 2, 3, or 4 (default 1)
651
- clip: { x: 0, y: 0, width: 100, height: 100 }, // Clip region in CSS pixels
652
- });
653
-
654
- // Export as SVG string
655
- const svgString = await framer.exportSVG(node.id);
656
- await require("fs").promises.writeFile(os.tmpdir() + "/export.svg", svgString);
657
- ```
658
-
659
- ### Publishing and Deployments
660
-
661
- Server API exclusive methods for publishing and managing deployments.
662
-
663
- ```js
664
- // Publish the project (creates a new deployment)
665
- const result = await framer.publish();
666
- console.log(result.deployment.id); // Deployment ID
667
- console.log(result.hostnames); // Array of hostnames
668
-
669
- // Get all deployments
670
- const deployments = await framer.getDeployments();
671
- for (const d of deployments) {
672
- console.log(d.id, d.createdAt);
673
- }
674
-
675
- // Deploy a specific deployment to domains
676
- const hostnames = await framer.deploy(deploymentId, ["example.com"]);
677
-
678
- // Get current publish info (production and staging URLs)
679
- const info = await framer.getPublishInfo();
680
- console.log(info.production?.url); // Production URL
681
- console.log(info.staging?.url); // Staging URL
682
- ```
683
-
684
- ### Change Tracking
685
-
686
- Server API exclusive methods for tracking project changes.
687
-
688
- ```js
689
- // Get changed paths since last publish
690
- const changes = await framer.getChangedPaths();
691
- console.log(changes.added); // New paths
692
- console.log(changes.modified); // Modified paths
693
- console.log(changes.removed); // Removed paths
694
-
695
- // Get contributors to changes (returns user IDs)
696
- const contributors = await framer.getChangeContributors();
697
- ```
698
-
699
438
  ### Known Limitations
700
439
 
701
440
  - **Pages**: Cannot change the path of a page
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "framer-dalton",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "type": "module",
5
5
  "bin": "./dist/cli.js",
6
6
  "main": "./dist/cli.js",
@@ -23,7 +23,7 @@
23
23
  "@trpc/client": "^11.9.0",
24
24
  "@trpc/server": "^11.9.0",
25
25
  "commander": "^12.1.0",
26
- "framer-api": "0.1.7",
26
+ "framer-api": "^0.1.8",
27
27
  "zod": "^4.3.6"
28
28
  },
29
29
  "devDependencies": {