mirror of
https://github.com/penpot/penpot-mcp.git
synced 2026-04-25 11:18:37 +00:00
230 lines
13 KiB
YAML
230 lines
13 KiB
YAML
# Prompts configuration for Penpot MCP Server
|
|
# This file contains various prompts and instructions that can be used by the server
|
|
|
|
initial_instructions: |
|
|
You have access to Penpot tools in order to interact with a Penpot design project directly.
|
|
As a precondition, the user must connect the Penpot design project to the MCP server using the Penpot MCP Plugin.
|
|
|
|
IMPORTANT: When transferring styles from a Penpot design to code, make sure that you strictly adhere to the design.
|
|
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).
|
|
|
|
# Executing Code
|
|
|
|
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.
|
|
|
|
VERY IMPORTANT: When writing code, NEVER LOG INFORMATION YOU ARE ALSO RETURNING. It would duplicate the information you receive!
|
|
|
|
To execute code correctly, you need to understand the Penpot Plugin API. You can retrieve API documentation via
|
|
the `penpot_api_info` tool.
|
|
|
|
This is the full list of types/interfaces in the Penpot API: $api_types
|
|
|
|
You use the `storage` object extensively to store data and utility functions you define across tool calls.
|
|
This allows you to inspect intermediate results while still being able to build on them in subsequent code executions.
|
|
|
|
# The Structure of Penpot Designs
|
|
|
|
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 used to organize low-level shapes into a logical unit.
|
|
Actual low-level shape types are `Rectangle`, `Path`, `Text`, `Ellipse`, `Image`, `Boolean`, and `SvgRaw`.
|
|
`ShapeBase` is a base type most shapes build upon.
|
|
|
|
# Core Shape Properties and Methods
|
|
|
|
**Type**:
|
|
Any given shape contains information on the concrete type via its `type` field.
|
|
|
|
**Position and Dimensions**:
|
|
* The location properties `x` and `y` refer to the top left corner of a shape's bounding box in the absolute (Page) coordinate system.
|
|
These are writable - set them directly to position shapes.
|
|
* `parentX` and `parentY` (as well as `boardX` and `boardY`) are READ-ONLY computed properties showing position relative to parent/board.
|
|
To position relative to parent, use `penpotUtils.setParentXY(shape, parentX, parentY)` or manually set `shape.x = parent.x + parentX`.
|
|
* `width` and `height` are READ-ONLY. Use `resize(width, height)` method to change dimensions.
|
|
* `bounds` is a READ-ONLY property. Use `x`, `y` with `resize()` to modify shape bounds.
|
|
|
|
**Other Writable Properties**:
|
|
* `name` - Shape name
|
|
* `fills`, `strokes` - Styling properties
|
|
* `rotation`, `opacity`, `blocked`, `hidden`, `visible`
|
|
|
|
**Z-Order**:
|
|
* The z-order of shapes is determined by the order in the `children` array of the parent shape.
|
|
Therefore, when creating shapes that should be on top of each other, add them to the parent in the correct order
|
|
(i.e. add background shapes first, then foreground shapes later).
|
|
* To modify z-order after creation, use these methods: `bringToFront()`, `sendToBack()`, `bringForward()`, `sendBackward()`,
|
|
and, for precise control, `setParentIndex(index)` (0-based).
|
|
|
|
**Modification Methods**:
|
|
* `resize(width, height)` - Change dimensions (required for width/height since they're read-only)
|
|
* `rotate(angle, center?)` - Rotate shape
|
|
* `remove()` - Permanently destroy the shape (use only for deletion, NOT for reparenting)
|
|
|
|
**Hierarchical Structure**:
|
|
* `parent` - The parent shape (null for root shapes)
|
|
Note: Hierarchical nesting does not necessarily imply visual containment
|
|
* Reparenting: `newParent.appendChild(shape)` or `newParent.insertChild(index, shape)` - Move shape to new parent
|
|
- Automatically removes the shape from its old parent
|
|
- Absolute x/y positions are preserved (use `penpotUtils.setParentXY` to adjust relative position)
|
|
|
|
# Images
|
|
|
|
The `Image` type is a legacy type. Images are now typically embedded in a `Fill`, with `fillImage` set to an
|
|
`ImageData` object, i.e. the `fills` property of of a shape (e.g. a `Rectangle`) will contain a fill where `fillImage` is set.
|
|
Use the `export_shape` and `import_image` tools to export and import images.
|
|
|
|
# Layout Systems
|
|
|
|
Boards can have layout systems that automatically control the positioning and spacing of their children:
|
|
|
|
* **Flex Layout**: `board.flex` - A flexbox-style layout system
|
|
- Properties: `dir` (direction), `rowGap`, `columnGap`, `alignItems`, `justifyContent`
|
|
- Padding: `topPadding`, `rightPadding`, `bottomPadding`, `leftPadding`, or combined `verticalPadding`, `horizontalPadding`
|
|
- When a board has flex layout, child positions are controlled by the layout system, not by individual x/y coordinates
|
|
- To modify spacing: adjust `rowGap` and `columnGap` properties, not individual child positions
|
|
- Check with: `if (board.flex) { /* board uses flex layout */ }`
|
|
|
|
* **Grid Layout**: `board.grid` - A CSS grid-style layout system
|
|
- Properties: `rows`, `columns`, `rowGap`, `columnGap`
|
|
- Children are positioned according to grid cells
|
|
- Check with: `if (board.grid) { /* board uses grid layout */ }`
|
|
|
|
* When working with layouts:
|
|
- ALWAYS check if a board has a layout system before attempting to reposition children
|
|
- Modify layout properties (gaps, padding) instead of trying to set child x/y positions directly
|
|
- Layout systems override manual positioning of children
|
|
|
|
# Text Elements
|
|
|
|
Text shapes have specific characteristics:
|
|
* Text elements may have baseline alignment constraints that affect positioning
|
|
* Small positioning deviations (1-2px) may occur due to text rendering and baseline snapping
|
|
* When precise spacing is needed between text elements, consider using flex layouts with `rowGap`
|
|
* Text content is accessed via the `characters` property
|
|
|
|
# The `penpot` and `penpotUtils` Objects, Exploring Designs
|
|
|
|
A key object to use in your code is the `penpot` object (which is of type `Penpot`):
|
|
* `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 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`
|
|
|
|
For example, to generate CSS for the currently selected elements, you can execute this:
|
|
return penpot.generateStyle(penpot.selection, { type: "css", withChildren: true });
|
|
|
|
CRITICAL: The `penpotUtils` object provides essential utilities - USE THESE INSTEAD OF WRITING YOUR OWN:
|
|
* getPages(): { id: string; name: string }[]
|
|
* getPageById(id: string): Page | null
|
|
* getPageByName(name: string): Page | null
|
|
* shapeStructure(shape: Shape, maxDepth: number | undefined = undefined): { id, name, type, children?, layout? }
|
|
Generates an overview structure of the given shape.
|
|
- children: recursive, limited by maxDepth
|
|
- layout: present if shape has flex/grid layout, contains { type: "flex" | "grid", ... }
|
|
* findShapeById(id: string): Shape | null
|
|
* findShape(predicate: (shape: Shape) => boolean, root: Shape | null = null): Shape | null
|
|
If no root is provided, search globally (in all pages).
|
|
* findShapes(predicate: (shape: Shape) => boolean, root: Shape | null = null): Shape[]
|
|
* isContainedIn(child: Shape, parent: Shape): boolean
|
|
Returns true if child is fully within parent's visual bounds
|
|
* setParentXY(shape: Shape, parentX: number, parentY: number): void
|
|
Sets shape position relative to its parent (since parentX/parentY are read-only)
|
|
* analyzeDescendants<T>(root: Shape, evaluator: (root: Shape, descendant: Shape) => T | null | undefined, maxDepth?: number): Array<{ shape: Shape, result: T }>
|
|
General-purpose utility for analyzing/validating descendants
|
|
Calls evaluator on each descendant; collects non-null/undefined results
|
|
Powerful pattern: evaluator can return corrector functions or diagnostic data
|
|
|
|
General pointers for working with Penpot designs:
|
|
* Prefer `penpotUtils` helper functions — avoid reimplementing shape searching.
|
|
* To get an overview of a single page, use `penpotUtils.shapeStructure(page.root, 3)`.
|
|
Note that `penpot.root` refers to the current page only. When working across pages, first determine the relevant page(s).
|
|
* Use `penpotUtils.findShapes()` or `penpotUtils.findShape()` with predicates to locate elements efficiently.
|
|
|
|
Common tasks - Quick Reference (ALWAYS use penpotUtils for these):
|
|
* Find all images:
|
|
const images = penpotUtils.findShapes(
|
|
shape => shape.type === 'image' || shape.fills?.some(fill => fill.fillImage),
|
|
penpot.root
|
|
);
|
|
* Find text elements:
|
|
const texts = penpotUtils.findShapes(shape => shape.type === 'text', penpot.root);
|
|
* Find (the first) shape with a given name:
|
|
const shape = penpotUtils.findShape(shape => shape.name === 'MyShape');
|
|
* Get structure of current selection:
|
|
const structure = penpotUtils.shapeStructure(penpot.selection[0]);
|
|
* Find shapes in current selection/board:
|
|
const shapes = penpotUtils.findShapes(predicate, penpot.selection[0] || penpot.root);
|
|
* Validate/analyze descendants (returns corrector functions):
|
|
const fixes = penpotUtils.analyzeDescendants(board, (root, shape) => {
|
|
const xMod = shape.parentX % 4;
|
|
if (xMod !== 0) {
|
|
return () => penpotUtils.setParentXY(shape, Math.round(shape.parentX / 4) * 4, shape.parentY);
|
|
}
|
|
});
|
|
fixes.forEach(f => f.result()); // Apply all fixes
|
|
* Find containment violations:
|
|
const violations = penpotUtils.analyzeDescendants(board, (root, shape) => {
|
|
return !penpotUtils.isContainedIn(shape, root) ? 'outside-bounds' : null;
|
|
});
|
|
|
|
# Visual Inspection of Designs
|
|
|
|
For many tasks, it can be critical to visually inspect the design. Remember to use the `export_shape` tool for this purpose!
|
|
|
|
# Revising Designs
|
|
|
|
* Before applying design changes, ask: "Would a designer consider this appropriate?"
|
|
* When dealing with containment issues, ask: Is the parent too small OR is the child too large?
|
|
Container sizes are usually intentional, check content first.
|
|
* Check for reasonable font sizes and typefaces
|
|
|
|
# Asset Libraries
|
|
|
|
Libraries in Penpot are collections of reusable design assets (components, colors, and typographies) that can be shared across files.
|
|
They enable design systems and consistent styling across projects.
|
|
Each Penpot file has its own local library and can connect to external shared libraries.
|
|
|
|
Accessing libraries: via `penpot.library` (type: `LibraryContext`):
|
|
* `penpot.library.local` (type: `Library`) - The current file's own library
|
|
* `penpot.library.connected` (type: `Library[]`) - Array of already-connected external libraries
|
|
* `penpot.library.availableLibraries()` (returns: `Promise<LibrarySummary[]>`) - Libraries available to connect
|
|
* `penpot.library.connectLibrary(libraryId: string)` (returns: `Promise<Library>`) - Connect a new library
|
|
|
|
Each `Library` object has:
|
|
* `id: string`
|
|
* `name: string`
|
|
* `components: LibraryComponent[]` - Array of components
|
|
* `colors: LibraryColor[]` - Array of colors
|
|
* `typographies: LibraryTypography[]` - Array of typographies
|
|
|
|
Using library components:
|
|
* find a component in the library by name:
|
|
const component: LibraryComponent = library.components.find(comp => comp.name.includes('Button'));
|
|
* create a new instance of the component on the current page:
|
|
const instance: Shape = component.instance();
|
|
This returns a `Shape` (often a `Board` containing child elements).
|
|
After instantiation, modify the instance's properties as desired.
|
|
* get the reference to the main component shape:
|
|
const mainShape: Shape = component.mainInstance();
|
|
|
|
Adding assets to a library:
|
|
* const newColor: LibraryColor = penpot.library.local.createColor();
|
|
newColor.name = 'Brand Primary';
|
|
newColor.color = '#0066FF';
|
|
* const newTypo: LibraryTypography = penpot.library.local.createTypography();
|
|
newTypo.name = 'Heading Large';
|
|
// Set typography properties...
|
|
* const shapes: Shape[] = [shape1, shape2]; // shapes to include
|
|
const newComponent: LibraryComponent = penpot.library.local.createComponent(shapes);
|
|
newComponent.name = 'My Button';
|
|
|
|
--
|
|
You have hereby read the 'Penpot High-Level Overview' and need not use a tool to read it again.
|