mirror of
https://github.com/penpot/penpot.git
synced 2026-09-06 20:18:39 +00:00
✨ Apply method Penpot.waitForLayoutUpdate, removing workarounds (#11433)
* MCP server: Update instruction manual to point to method instead of sleep * MCP plugin: Call waitForLayoutUpdate prior to every code execution * Composable test suite: Use waitForLayoutUpdate instead of 200ms delay in tests
This commit is contained in:
parent
292390f0eb
commit
36cf4f39e1
@ -214,6 +214,17 @@ export class ExecuteCodeTaskHandler extends TaskHandler<ExecuteCodeTaskParams> {
|
|||||||
|
|
||||||
let result: any;
|
let result: any;
|
||||||
try {
|
try {
|
||||||
|
// wait for layout updates prior to executing the supplied code (if method is available)
|
||||||
|
try {
|
||||||
|
// @ts-ignore - TODO Penpot.waitForLayoutUpdate is not yet in the released types
|
||||||
|
if (penpot.waitForLayoutUpdate) {
|
||||||
|
// @ts-ignore
|
||||||
|
await penpot.waitForLayoutUpdate();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error waiting for layout update:", e);
|
||||||
|
}
|
||||||
|
|
||||||
// execute the code in an async function with the context variables as parameters
|
// execute the code in an async function with the context variables as parameters
|
||||||
result = await (async (ctx) => {
|
result = await (async (ctx) => {
|
||||||
const fn = new Function(...Object.keys(ctx), `return (async () => { ${code} })();`);
|
const fn = new Function(...Object.keys(ctx), `return (async () => { ${code} })();`);
|
||||||
|
|||||||
@ -16,6 +16,14 @@ 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.
|
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.
|
This allows you to inspect intermediate results while still being able to build on them in subsequent code executions.
|
||||||
|
|
||||||
|
## Asynchronous Updates
|
||||||
|
|
||||||
|
Changes made to a design may take effect asynchronously.
|
||||||
|
So if you need to read the result of your executions/observe properties affected by a change in the same `execude_code` call, use
|
||||||
|
`await penpot.waitForLayoutUpdate();`
|
||||||
|
before trying to observe changes.
|
||||||
|
Every `execude_code` call waits for updates before applying your code, so you never need to call `waitForLayoutUpdate` at the beginning of your code.
|
||||||
|
|
||||||
# The Structure of Penpot Designs
|
# The Structure of Penpot Designs
|
||||||
|
|
||||||
A Penpot design ultimately consists of shapes.
|
A Penpot design ultimately consists of shapes.
|
||||||
@ -144,7 +152,7 @@ Boards can have layout systems that automatically control the positioning and sp
|
|||||||
it only changes the formal bounding box; if the text does not fit it, it will overflow; use `textBounds` for the actual bounding box of the rendered text.
|
it only changes the formal bounding box; if the text does not fit it, it will overflow; use `textBounds` for the actual bounding box of the rendered text.
|
||||||
* Property `bounds` is sized automatically (in one dimension) if the `growType` property is set to "auto-width" or "auto-height".
|
* Property `bounds` is sized automatically (in one dimension) if the `growType` property is set to "auto-width" or "auto-height".
|
||||||
`resize` always sets `growType` to "fixed", so ALWAYS set it back to "auto-width" or "auto-height" if you want automatic sizing!
|
`resize` always sets `growType` to "fixed", so ALWAYS set it back to "auto-width" or "auto-height" if you want automatic sizing!
|
||||||
The auto-sizing is not immediate; sleep for a short time (100ms) if you want to read the updated bounding box.
|
The auto-sizing is asynchronous; use `waitForLayoutUpdate` before reading the updated bounding box.
|
||||||
* Method `getRange(start, end): TextRange` to reference a range of characters as a `TextRange` object, which can be styled separately from the rest of the text; `start` index inclusive, `end` exclusive
|
* Method `getRange(start, end): TextRange` to reference a range of characters as a `TextRange` object, which can be styled separately from the rest of the text; `start` index inclusive, `end` exclusive
|
||||||
* Other Writable font properties: `fontId`, `fontFamily`, `fontWeight`, `fontVariant`, `fontStyle`
|
* Other Writable font properties: `fontId`, `fontFamily`, `fontWeight`, `fontVariant`, `fontStyle`
|
||||||
- To discover valid values, check available fonts in `penpot.fonts: FontContext`
|
- To discover valid values, check available fonts in `penpot.fonts: FontContext`
|
||||||
@ -391,7 +399,7 @@ Applying tokens:
|
|||||||
- TokenTextDecorationProps: "textDecoration"
|
- TokenTextDecorationProps: "textDecoration"
|
||||||
- TokenTypographyProps: "typography"
|
- TokenTypographyProps: "typography"
|
||||||
* `token.applyToShapes(shapes, properties)` - Apply from token
|
* `token.applyToShapes(shapes, properties)` - Apply from token
|
||||||
* Application is **asynchronous** (wait for ~100ms to see the effects)
|
* Application is **asynchronous** (use `waitForLayoutUpdate`)
|
||||||
* After application:
|
* After application:
|
||||||
- `shape.tokens` returns a mapping `{ propertyName: "token.name" }` from `TokenProperty` to token name
|
- `shape.tokens` returns a mapping `{ propertyName: "token.name" }` from `TokenProperty` to token name
|
||||||
- The actual shape properties that the tokens control will reflect the token's resolved value.
|
- The actual shape properties that the tokens control will reflect the token's resolved value.
|
||||||
|
|||||||
@ -27,13 +27,8 @@ export class OpAssert extends Operation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async applyTo(situation: Situation): Promise<void> {
|
async applyTo(situation: Situation): Promise<void> {
|
||||||
try {
|
await PenpotSync.awaitPropagation();
|
||||||
this.assertion(situation);
|
this.assertion(situation);
|
||||||
} catch {
|
|
||||||
// a read may have raced propagation; let it settle and check once more
|
|
||||||
await PenpotSync.awaitPropagation();
|
|
||||||
this.assertion(situation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(): string {
|
toString(): string {
|
||||||
|
|||||||
@ -12,6 +12,13 @@ export class PenpotSync {
|
|||||||
* explicit "wait for propagation" primitive.
|
* explicit "wait for propagation" primitive.
|
||||||
*/
|
*/
|
||||||
static awaitPropagation(): Promise<void> {
|
static awaitPropagation(): Promise<void> {
|
||||||
return new Promise((resolve) => setTimeout(resolve, PenpotSync.PROPAGATION_MS));
|
// @ts-ignore
|
||||||
|
if (penpot.waitForLayoutUpdate) {
|
||||||
|
// @ts-ignore
|
||||||
|
return penpot.waitForLayoutUpdate();
|
||||||
|
} else {
|
||||||
|
throw new Error("PenpotSync.awaitPropagation: waitForLayoutUpdate is not available");
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, PenpotSync.PROPAGATION_MS));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user