mirror of
https://github.com/penpot/penpot-mcp.git
synced 2026-04-25 11:18:37 +00:00
Add PenpotUtils.analyzeDescendants as a powerful utility function for validation #26
This commit is contained in:
parent
c4c37adb25
commit
90459f0ba4
@ -152,6 +152,10 @@ initial_instructions: |
|
||||
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: (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.
|
||||
@ -173,6 +177,18 @@ initial_instructions: |
|
||||
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, (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, (shape) => {
|
||||
return !penpotUtils.isContainedIn(shape, board) ? 'outside-bounds' : null;
|
||||
});
|
||||
|
||||
# Asset Libraries
|
||||
|
||||
|
||||
@ -198,6 +198,48 @@ export class PenpotUtils {
|
||||
shape.y = shape.parent.y + parentY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyzes all descendants of a shape by applying an evaluator function to each.
|
||||
* Only descendants for which the evaluator returns a non-null/non-undefined value are included in the result.
|
||||
* This is a general-purpose utility for validation, analysis, or collecting corrector functions.
|
||||
*
|
||||
* @param root - The root shape whose descendants to analyze
|
||||
* @param evaluator - Function called for each descendant; return null/undefined to skip
|
||||
* @param maxDepth - Optional maximum depth to traverse (undefined for unlimited)
|
||||
* @returns Array of objects containing the shape and the evaluator's result
|
||||
*/
|
||||
public static analyzeDescendants<T>(
|
||||
root: Shape,
|
||||
evaluator: (descendant: Shape) => T | null | undefined,
|
||||
maxDepth: number | undefined = undefined
|
||||
): Array<{ shape: Shape; result: NonNullable<T> }> {
|
||||
const results: Array<{ shape: Shape; result: NonNullable<T> }> = [];
|
||||
|
||||
const traverse = (shape: Shape, currentDepth: number): void => {
|
||||
const result = evaluator(shape);
|
||||
if (result !== null && result !== undefined) {
|
||||
results.push({ shape, result: result as NonNullable<T> });
|
||||
}
|
||||
|
||||
if (maxDepth === undefined || currentDepth < maxDepth) {
|
||||
if ("children" in shape && shape.children) {
|
||||
for (const child of shape.children) {
|
||||
traverse(child, currentDepth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start traversal with root's children (not root itself)
|
||||
if ("children" in root && root.children) {
|
||||
for (const child of root.children) {
|
||||
traverse(child, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base64 string to a Uint8Array.
|
||||
* This is required because the Penpot plugin environment does not provide the atob function.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user