mirror of
https://github.com/penpot/penpot.git
synced 2026-09-01 17:49:08 +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;
|
||||
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
|
||||
result = await (async (ctx) => {
|
||||
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.
|
||||
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
|
||||
|
||||
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.
|
||||
* 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!
|
||||
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
|
||||
* Other Writable font properties: `fontId`, `fontFamily`, `fontWeight`, `fontVariant`, `fontStyle`
|
||||
- To discover valid values, check available fonts in `penpot.fonts: FontContext`
|
||||
@ -391,7 +399,7 @@ Applying tokens:
|
||||
- TokenTextDecorationProps: "textDecoration"
|
||||
- TokenTypographyProps: "typography"
|
||||
* `token.applyToShapes(shapes, properties)` - Apply from token
|
||||
* Application is **asynchronous** (wait for ~100ms to see the effects)
|
||||
* Application is **asynchronous** (use `waitForLayoutUpdate`)
|
||||
* After application:
|
||||
- `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.
|
||||
|
||||
@ -27,13 +27,8 @@ export class OpAssert extends Operation {
|
||||
}
|
||||
|
||||
async applyTo(situation: Situation): Promise<void> {
|
||||
try {
|
||||
this.assertion(situation);
|
||||
} catch {
|
||||
// a read may have raced propagation; let it settle and check once more
|
||||
await PenpotSync.awaitPropagation();
|
||||
this.assertion(situation);
|
||||
}
|
||||
await PenpotSync.awaitPropagation();
|
||||
this.assertion(situation);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
||||
@ -12,6 +12,13 @@ export class PenpotSync {
|
||||
* explicit "wait for propagation" primitive.
|
||||
*/
|
||||
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