Add utils for Page handling, improve prompts explaining design structure

This commit is contained in:
Dominik Jain 2025-09-30 15:10:37 +02:00 committed by Dominik Jain
parent cf5231db66
commit dacd87bbac
2 changed files with 58 additions and 4 deletions

View File

@ -9,7 +9,14 @@ initial_instructions: |
NEVER make assumptions about missing values and don't get overly creative (e.g. don't pick your own colours and stick to
non-creative defaults such as white/black if you are lacking information).
A Penpot project contains shapes and more general design elements (which we shall collectively refer to as "elements").
A Penpot design ultimately consists of shapes.
The type `Shape` is a union type, which encompasses both containers and low-level shapes.
Shapes in a Penpot design are organized hierarchically.
At the top level, a design project contains one or more `Page` objects.
Each `Page` contains a tree of elements. For a given instance `page`, its root shape is `page.root`.
A Page is frequently structured into boards. A `Board` is a high-level grouping element.
A `Group` is a more low-level grouping element to organize low-level shapes into a logical unit.
Low-level shape types are `Rectangle`, `Path`, `Text`, `Ellipse`, `Image`, etc.
One of your key tools is the `execute_code` tool, which allows you to run JavaScript code using the Penpot Plugin API
directly in the connected project.
@ -17,9 +24,9 @@ initial_instructions: |
the `penpot_api_info` tool.
When writing code, a key object is the `penpot` object (which is of type `Penpot`):
* `penpot.selection` provides the list of elements the user has selected in the Penpot UI.
* `penpot.selection` provides the list of shapes the user has selected in the Penpot UI.
If it is unclear which elements to work on, you can ask the user to select them for you.
* `penpot.root` provides the root element of the tree-structured design project, from which all shapes can be reached.
* `penpot.root` provides the root shape of the currently active page.
* Generation of CSS content for elements via `penpot.generateStyle`
* Generation of HTML/SVG content for elements via `penpot.generateMarkup`
@ -31,6 +38,9 @@ initial_instructions: |
Any given shape contains information on the concrete type via its `type` field.
Another useful object is the `penpotUtils` object (which is not part of the official API). It provides:
* getPages(): { id: string; name: string }[]
* getPageById(id: string): Page | null
* getPageByName(name: string): Page | null
* shapeStructure(shape: Shape, maxDepth: number | undefined = undefined): object
Generates an overview structure of the given shape,
providing the shape's id, name and type, and recursively the children's structure.

View File

@ -1,4 +1,4 @@
import { Shape } from "@penpot/plugin-types";
import { Page, Shape } from "@penpot/plugin-types";
export class PenpotUtils {
/**
@ -27,6 +27,33 @@ export class PenpotUtils {
};
}
/**
* Finds all shapes that matches the given predicate in the given shape tree.
*
* @param predicate - A function that takes a shape and returns true if it matches the criteria
* @param root - The root shape to start the search from (defaults to penpot.root)
*/
public static findShapes(predicate: (shape: Shape) => boolean, root: Shape | null = penpot.root): Shape[] {
let result = new Array<Shape>();
let find = function (shape: Shape | null) {
if (!shape) {
return;
}
if (predicate(shape)) {
result.push(shape);
}
if ("children" in shape && shape.children) {
for (let child of shape.children) {
find(child);
}
}
};
find(root);
return result;
}
/**
* Finds the first shape that matches the given predicate in the given shape tree.
*
@ -64,4 +91,21 @@ export class PenpotUtils {
public static findShapeById(id: string): Shape | null {
return this.findShape((shape) => shape.id === id);
}
public static findPage(predicate: (page: Page) => boolean): Page | null {
let page = penpot.currentFile!.pages.find(predicate);
return page || null;
}
public static getPages(): { id: string; name: string }[] {
return penpot.currentFile!.pages.map((page) => ({ id: page.id, name: page.name }));
}
public static getPageById(id: string): Page | null {
return this.findPage((page) => page.id === id);
}
public static getPageByName(name: string): Page | null {
return this.findPage((page) => page.name.toLowerCase() === name.toLowerCase());
}
}