From ed935e533f4f8789d87acd7098559138664f7980 Mon Sep 17 00:00:00 2001 From: Dominik Jain Date: Sat, 2 May 2026 14:26:32 +0200 Subject: [PATCH] :sparkles: Expose variants retrieval via isVariant() type guard on LibraryComponent Change isVariant() return type from boolean to 'this is LibraryVariantComponent', enabling TypeScript users to directly access variants, variantProps, and variantError after a type-narrowing check. Update MCP instructions with improved variant navigation guidance. Closes #9185 Co-authored-by: Claude (Anthropic) --- CHANGES.md | 1 + mcp/packages/server/data/initial_instructions.md | 7 +++++-- plugins/CHANGELOG.md | 1 + plugins/libs/plugin-types/index.d.ts | 6 ++++-- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 149fc49f05..547117e66e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -62,6 +62,7 @@ - Add Shift+Numpad0/1/2 as aliases to Shift+0/1/2 for zoom shortcuts (by @RenzoMXD) [Github #9063](https://github.com/penpot/penpot/pull/9063) - Add pixel grid color picker in viewport settings (by @Yakehira) [Github #7750](https://github.com/penpot/penpot/issues/7750) - Add HEX, HSB and HSL support to the color picker with a model switcher that persists across sessions (by @edwin-rivera-dev) [Github #9133](https://github.com/penpot/penpot/issues/9133) +- Expose `variants` retrieval on `LibraryComponent` via `isVariant()` type guard in plugin API [Github #9185](https://github.com/penpot/penpot/issues/9185) - Show specific invitation-link error messages for expired, email-mismatch and invalid token cases [Github #9220](https://github.com/penpot/penpot/issues/9220) - Show detailed messages on file import errors to help diagnose why a file could not be imported (by @jsdevninja) [Github #9004](https://github.com/penpot/penpot/issues/9004) - Add read-only preview mode for saved versions — click a version name to open a dedicated preview view (by @wdeveloper16) [Github #8976](https://github.com/penpot/penpot/issues/8976) diff --git a/mcp/packages/server/data/initial_instructions.md b/mcp/packages/server/data/initial_instructions.md index dbd2f565b7..0c129d6f38 100644 --- a/mcp/packages/server/data/initial_instructions.md +++ b/mcp/packages/server/data/initial_instructions.md @@ -269,8 +269,9 @@ Using library components: * 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: + - After instantiation, modify the instance's properties as desired. + - Get a reference to the component an instance was created from via `instance.component()`. + * get the reference to the main instance (shape that serves as the source for new instances): `const mainShape: Shape = component.mainInstance();` Adding a component to a library: @@ -295,6 +296,7 @@ Variants are a system for grouping related component versions along named proper - `properties: string[]` (ordered list of property names); `addProperty(): void`, `renameProperty(pos, name)`, `currentValues(property)` - `variantComponents(): LibraryVariantComponent[]` * `LibraryVariantComponent` (extends `LibraryComponent`): full library component with metadata, for which `isVariant()` returns true. + - `variants: Variants` - `variantProps: { [property: string]: string }` (this component's value for each property) - `variantError` (non-null if e.g. two variants share the same combination of property values) - `setVariantProperty(pos, value)` @@ -313,6 +315,7 @@ Use `variantContainer.appendChild(mainInstance)` to move a component's main inst **Using Variants**: - `compInstance.switchVariant(pos, value)`: On a component instance, switches to the nearest variant that has the given value at property position `pos`, keeping all other property values the same. - To instantiate a specific variant, find the right `LibraryVariantComponent` by checking `variantProps`, then call `.instance()`. +- Given a variant component instance, access the component it was instantiated from via `instance.component()` and the `Variants` instance via `instance.component().variants`. # Design Tokens diff --git a/plugins/CHANGELOG.md b/plugins/CHANGELOG.md index 4b58244e86..cb2a2400e4 100644 --- a/plugins/CHANGELOG.md +++ b/plugins/CHANGELOG.md @@ -3,6 +3,7 @@ - **plugins-runtime**: Added `version` field that returns the current version - **plugin-types**: Added a flags subcontexts with the flag `naturalChildrenOrdering` - **plugin-types**: Fix penpot.openPage() to navigate in same tab by default +- **plugin-types:** Change `LibraryComponent.isVariant()` return type to type guard `this is LibraryVariantComponent` - **plugin-types**: Added `createVariantFromComponents` - **plugin-types**: Change return type of `combineAsVariants` - **plugin-types**: Added `textBounds` property for text shapes diff --git a/plugins/libs/plugin-types/index.d.ts b/plugins/libs/plugin-types/index.d.ts index dd42f365fe..b2d810a17d 100644 --- a/plugins/libs/plugin-types/index.d.ts +++ b/plugins/libs/plugin-types/index.d.ts @@ -2672,9 +2672,11 @@ export interface LibraryComponent extends LibraryElement { mainInstance(): Shape; /** - * @return true when this component is a VariantComponent + * Checks whether this component is a variant component. + * If true, the component can be used as a `LibraryVariantComponent`, + * which provides additional attributes (e.g. `variants`) and methods. */ - isVariant(): boolean; + isVariant(): this is LibraryVariantComponent; /** * Creates a new Variant from this standard Component. It creates a VariantContainer, transform this Component into a VariantComponent, duplicates it, and creates a