diff --git a/skills/generating-custom-lightning-type/SKILL.md b/skills/generating-custom-lightning-type/SKILL.md index bdf2fb16..dadbe6e4 100644 --- a/skills/generating-custom-lightning-type/SKILL.md +++ b/skills/generating-custom-lightning-type/SKILL.md @@ -1,6 +1,6 @@ --- name: generating-custom-lightning-type -description: "Use this skill when users need to create Custom Lightning Types (CLTs) for Einstein Agent actions or structured input/output schemas. Trigger when users mention CLT, Custom Lightning Types, JSON schemas for agents, type definitions, lightning__objectType, or editor/renderer configurations. This is complex - always use this skill for CLT work." +description: "Use this skill when users need to create Custom Lightning Types (CLTs) for Einstein Agent actions or structured input/output schemas. Trigger when users mention CLT, Custom Lightning Types, Custom Lightning Types (CLTs) with widget/mosaic/fragment rendition/renderer, JSON schemas for agents, type definitions, lightning__objectType, or editor/renderer configurations. When widget renditions are requested, you MUST first read the widget-rendition.md reference file in this skill's references/ directory and follow its complete workflow. This is complex - always use this skill for CLT work." metadata: version: "1.0" --- @@ -12,6 +12,7 @@ Use this skill when you need to: - Generate JSON Schema-based type definitions for Lightning Platform - Configure CLTs for Einstein Agent actions - Set up editor and renderer configurations for custom UI +- Create CLTs with widget/mosaic/fragment rendition - Troubleshoot deployment errors related to Custom Lightning Types ## Specification @@ -26,6 +27,7 @@ Custom Lightning Types (CLTs) are JSON Schema-based type definitions used by the - **Choose standard Lightning types** when the structure is simple and can be expressed with properties and supported primitive `lightning:type` identifiers. - **Choose Apex class types** (`@apexClassType/...`) when the structure already exists server-side and you want the Apex class to define the shape. - **Include editor/renderer config** only when you need custom UI behavior (custom LWC input/output components). Otherwise, omit. +- **Widget rendition**: After `schema.json` is drafted, you **must read references/widget-rendition.md and follow the "Widget renderer pattern"** section below for complete guidance. Do NOT attempt widget generation without reading the reference file first. ## Critical Rules (Read First) - **Root object schemas MUST include**: @@ -127,7 +129,7 @@ When strict validation is enabled (`unevaluatedProperties: false`), keep each pr - **Avoid known-invalid patterns**: - Do not use `es_property_editors/inputList`. - Do not use `itemSchema` attributes. -4. **(Optional) Draft `renderer.json`** (only if custom UI is required) +4. **(Optional) Draft `renderer.json`** (only if custom UI or mosaic rendition is required) - **Supported shape:** Top-level `renderer` object with `renderer.componentOverrides` and `renderer.layout`. - Top-level `renderer` object. - Use `renderer.componentOverrides` for component overrides. @@ -138,6 +140,15 @@ When strict validation is enabled (`unevaluatedProperties: false`), keep each pr - Use `{!$attrs.}` in attribute mappings when binding schema data to custom renderer component attributes. - **CRITICAL**: Attribute mappings like `{!$attrs.propertyName}` must reference properties that **actually exist** in your type schema. Referencing non-existent properties will fail validation. - **Type matching**: Attribute values must match the expected type for the component. For example, if a component expects a string attribute, passing an integer will fail validation. + - **Widget renderer pattern** (for widget rendition): + - **When to use:** Use this when users request "mosaic", "widget", "fragment", or "cross-platform rendering" for their CLT. + - **Structure:** `renderer.componentOverrides["$"] = { "type": "mosaic", "definition": "tile/mosaic", "children": [ /* UEM tree of blocks and regions */ ] }` + - **REQUIRED workflow:** + - **STOP**: Do NOT attempt to create the widget renderer yourself. + - **MANDATORY FIRST STEP**: You MUST fetch the reference file `references/widget-rendition.md` located in this skill's directory before proceeding. + - Follow the complete workflow documented in `widget-rendition.md` using the generated CLT schema as the grounding schema. + - The `widget-rendition.md` reference contains the full widget generation workflow: discovering UEM blocks via discoverUiComponents, calling getUiComponentSchemas, building the UEM tree, and writing renderer.json. + - **Do not** attempt to generate widget rendition without first fetching the `widget-rendition.md` reference file. - **Property-level override pattern**: - `renderer.componentOverrides[""] = { "definition": "es_property_editors/outputText" | "es_property_editors/outputNumber" | "es_property_editors/outputImage" | ... }`. **Valid renderer components** (examples): `es_property_editors/outputText`, `es_property_editors/outputNumber`, `es_property_editors/outputImage`. Avoid input-style components in the renderer. - **Layout pattern for renderer**: diff --git a/skills/generating-custom-lightning-type/references/widget-rendition.md b/skills/generating-custom-lightning-type/references/widget-rendition.md new file mode 100644 index 00000000..454f12f9 --- /dev/null +++ b/skills/generating-custom-lightning-type/references/widget-rendition.md @@ -0,0 +1,124 @@ +# Widget Generation Guide + +## 📋 Overview +Widgets are reusable pieces of UI similar to templates, with placeholders for actual data values. The purpose of this file is to assist developers in creating mosaic renditions for CLTs. + +## 🎯 Purpose +Widgets render data in a structured and unified way across various Salesforce experiences like Slack, Mobile, LEX etc. + +## Schema Grounding +Widget generation is **always schema-grounded** using a CLT's schema. The schema describes the data shape the widget should render. Extract property names, types, required vs optional, and nesting from the schema; then follow the full **Workflow** below, using this extracted structure to guide every step. Do not add or remove properties relative to the schema. + +## ⚙️ Composition +A widget is a UEM (Unified Experience Model) tree of blocks and regions. The widget you return must follow the Typescript interfaces below: + +```ts +interface BlockType { + type: 'block' + definition: string // {namespace}/{blockName} + attributes?: Record + children?: (BlockType | RegionType)[] +} + +interface RegionType { + type: 'region' + name: string + children: BlockType[] +} +``` +--- + +## 🔧 Available Metadata Actions + +### When to Use Each Action + +#### discoverUiComponents + +**Purpose:** Discover the palette of available blocks that can be used in widget composition. + +**Use for:** Finding available blocks before building your widget structure. + +**Input Parameters:** +- `actionName` (**required***): "discoverUiComponents" +- `metadataType` (**required**): "FRAGMENT" +- `parameters` (**required**): JSON object with the below fields + - `pageType` (**required**): "FRAGMENT" + - `pageContext` (optional): JSON object - not required for FRAGMENT type + - `searchQuery` (optional): String to filter components by name or description + +**Returns:** List of components with: +- `definition`: Fully qualified name (e.g., "namespace/definiton") +- `description`: Component description +- `label`: Human-readable label +- `attributes`: Optional attribute metadata + +#### getUiComponentSchemas + +**Purpose:** Get detailed JSON schemas for component configuration, including property types, required vs optional fields, and validation rules. + +**Use for:** You know which components you want but need to understand their properties before adding them to your widget. + +**Input Parameters:** +- `actionName` (**required***): "getUiComponentSchemas" +- `metadataType` (**required**): "FRAGMENT" +- `parameters` (**required**): JSON object with the below fields + - `pageType` (**required**): "FRAGMENT" + - `componentDefinitions` (**required**): List of fully qualified names (e.g., ["namespace/definition"]) + - **CRITICAL**: NEVER include "tile/mosaic" in this list. "tile/mosaic" is a container component used in renderer.json structure and **should not** be passed to getUiComponentSchemas + - `pageContext` (optional): JSON object - not required for FRAGMENT type + - `includeKnowledge` (optional): Boolean, defaults to true - includes additional component-specific guidance + +**Returns:** +- `componentSchemas`: List of results (supports partial failures) +- **Success entries**: Contains JSON schema with property definitions, types, constraints +- **Failure entries**: Contains error message explaining why schema couldn't be retrieved +- `$defs`: Schema definitions and references (if schema transformation applied) + +**Key Feature:** Supports partial failures - if some components can't be found, you still get schemas for the successful ones. + +--- + +## Attribute binding using placeholder syntax + +- **Where to use:** When block properties must display or pass runtime data from the grounding schema, use the **Placeholder Syntax** below so that the runtime binds values into the widget. Check each block's schema (from `getUiComponentSchemas`) for the correct property name (e.g. `value`, `text`, `label`). +- **Placeholder Syntax:** Use `{!$attrs.}` as the placeholder for each block property that should receive data. + `` **must** match the property name from the grounding schema so that the runtime can resolve its value. + Example: for a schema property `title`, set the block property to `{!$attrs.title}`. +- **List / iterative data:** Only the children (list items) hold bound values; the parent list block does not. For each item inside a list (e.g. `tile/listItem`), use `{!$attrs..item}` so the runtime binds the current item. `` MUST match the schema property name of the list. Example: for `icons`, use `"{!$attrs.icons.item}"` on the list item. + +--- + +## 💡Workflow + +1. **Schema Parsing** +- Parse the schema and extract: property names, types, required vs optional, and nested structure. Use this as the **widget spec**. + +2. **Discover Available Blocks** (**REQUIRED** - do NOT skip) +- Use **discoverUiComponents metadata action** above to explore what blocks are available. +- Use property types from the **widget spec** to inform `searchQuery` (e.g. text → "text", number → "number"). + +3. **Select Components** +- Choose blocks that can represent each property in the **widget spec** from the results of step 2. + +4. **Get Component Schemas** (**REQUIRED** - do NOT skip) +- Use **getUiComponentSchemas metadata action** with the selected block definitions from step 3 and review block properties' metadata. + +5. **Build Widget** +- Construct the UEM tree. Map each property in the **widget spec** to block properties and preserve order of the **widget spec**. +- For block properties that must show or pass runtime data, use the placeholder syntax (see **Attribute binding using placeholder syntax** above). +- Use block properties from the schemas retrieved in step 4. + +6. **Write output to CLT Bundle** +- Always write to `lightningTypes//lightningDesktopGenAi/renderer.json` (or the correct target subfolder for the product surface, e.g. `experienceBuilder/`, `lightningMobileGenAi/`, `enhancedWebChat/` when applicable). + Check **required root override pattern** below - + `renderer.componentOverrides["$"] = { "type": "mosaic", "definition": "tile/mosaic", "children": [ ... ] — array of UEM nodes - contains the widget UEM generated using the **Workflow** steps 1-5 above }` + +--- + +## ⚠️ Important Notes + +- **widget spec** includes both required and optional attributes - review carefully to ensure valid configuration. +- When using **`execute_metadata_action`** tool, always supply **`parameters`** with the required fields above; missing `parameters` or required keys causes hard failures, not partial results. +- Block definitions always follow the `{namespace}/{blockName}` convention. +- Use the same definition format returned by `discoverUiComponents` when calling `getUiComponentSchemas` +- Placeholder syntax for non-list properties is `{!$attrs.}` and for list properties is `{!$attrs..item}`. \ No newline at end of file