@elementor/editor-canvas 3.35.0-470 → 3.35.0-471
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +459 -511
- package/dist/index.mjs +448 -496
- package/package.json +18 -18
- package/src/composition-builder/composition-builder.ts +233 -0
- package/src/mcp/mcp-description.ts +99 -108
- package/src/mcp/resources/widgets-schema-resource.ts +1 -1
- package/src/mcp/tools/build-composition/prompt.ts +104 -280
- package/src/mcp/tools/build-composition/schema.ts +0 -1
- package/src/mcp/tools/build-composition/tool.ts +49 -125
- package/src/mcp/utils/do-update-element-property.ts +12 -3
- package/src/mcp/utils/validate-input.ts +9 -2
package/dist/index.js
CHANGED
|
@@ -118,7 +118,7 @@ The css string must follow standard CSS syntax, with properties and values separ
|
|
|
118
118
|
"styles-schema",
|
|
119
119
|
new import_editor_mcp.ResourceTemplate(STYLE_SCHEMA_URI, {
|
|
120
120
|
list: () => {
|
|
121
|
-
const categories = [...Object.keys((0, import_editor_styles.getStylesSchema)())];
|
|
121
|
+
const categories = [...Object.keys((0, import_editor_styles.getStylesSchema)())].filter((category) => category !== "all");
|
|
122
122
|
return {
|
|
123
123
|
resources: categories.map((category) => ({
|
|
124
124
|
uri: `elementor://styles/schema/${category}`,
|
|
@@ -2240,6 +2240,9 @@ function extractElementData(element) {
|
|
|
2240
2240
|
}
|
|
2241
2241
|
|
|
2242
2242
|
// src/mcp/tools/build-composition/tool.ts
|
|
2243
|
+
var import_editor_elements8 = require("@elementor/editor-elements");
|
|
2244
|
+
|
|
2245
|
+
// src/composition-builder/composition-builder.ts
|
|
2243
2246
|
var import_editor_elements7 = require("@elementor/editor-elements");
|
|
2244
2247
|
|
|
2245
2248
|
// src/mcp/utils/do-update-element-property.ts
|
|
@@ -2247,7 +2250,11 @@ var import_editor_elements5 = require("@elementor/editor-elements");
|
|
|
2247
2250
|
var import_editor_props5 = require("@elementor/editor-props");
|
|
2248
2251
|
var import_editor_styles5 = require("@elementor/editor-styles");
|
|
2249
2252
|
function resolvePropValue(value, forceKey) {
|
|
2250
|
-
|
|
2253
|
+
const Utils = window.elementorV2.editorVariables.Utils;
|
|
2254
|
+
return import_editor_props5.Schema.adjustLlmPropValueSchema(value, {
|
|
2255
|
+
forceKey,
|
|
2256
|
+
transformers: Utils.globalVariablesLLMResolvers
|
|
2257
|
+
});
|
|
2251
2258
|
}
|
|
2252
2259
|
var doUpdateElementProperty = (params) => {
|
|
2253
2260
|
const { elementId, propertyName, propertyValue, elementType } = params;
|
|
@@ -2391,9 +2398,14 @@ var validateInput = {
|
|
|
2391
2398
|
} else if (!import_editor_props6.Schema.isPropKeyConfigurable(propName)) {
|
|
2392
2399
|
errors.push(`Property "${propName}" is not configurable.`);
|
|
2393
2400
|
} else {
|
|
2394
|
-
const { valid
|
|
2401
|
+
const { valid } = import_editor_props6.Schema.validatePropValue(propSchema, propValue);
|
|
2395
2402
|
if (!valid) {
|
|
2396
|
-
errors.push(
|
|
2403
|
+
errors.push(
|
|
2404
|
+
`Invalid property "${propName}". Validate input with resource [${STYLE_SCHEMA_URI.replace(
|
|
2405
|
+
"{category}",
|
|
2406
|
+
propName
|
|
2407
|
+
)}]`
|
|
2408
|
+
);
|
|
2397
2409
|
}
|
|
2398
2410
|
}
|
|
2399
2411
|
});
|
|
@@ -2432,290 +2444,292 @@ var validateInput = {
|
|
|
2432
2444
|
}
|
|
2433
2445
|
};
|
|
2434
2446
|
|
|
2435
|
-
// src/
|
|
2436
|
-
var
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
- NO generic sans-serifs as primary typefaces (Inter, Roboto, Arial, Helvetica)
|
|
2499
|
-
- NO timid size ratios (1.2x, 1.5x scaling)
|
|
2500
|
-
- NO uniform font weights (everything at 400 or 600)
|
|
2501
|
-
|
|
2502
|
-
**Intentional Alternatives:**
|
|
2503
|
-
- **For Technical/Modern**: Consider monospace headlines (JetBrains Mono, SF Mono) paired with clean body text
|
|
2504
|
-
- **For Editorial/Elegant**: Consider serif headlines (Playfair Display, Crimson Text) with sans-serif body
|
|
2505
|
-
- **For Playful/Creative**: Consider display fonts with character, paired with highly legible body text
|
|
2506
|
-
|
|
2507
|
-
**Scale & Contrast Implementation:**
|
|
2508
|
-
- Headline-to-body size ratios: 3x minimum (e.g., 48px headline vs 16px body)
|
|
2509
|
-
- Use extreme weight contrasts: pair weight-100 or 200 with weight-800 or 900
|
|
2510
|
-
- Line height contrasts: tight headlines (1.1) vs. generous body (1.7)
|
|
2511
|
-
- Letter spacing: compressed headlines (-0.02em to -0.05em) vs. open small text (0.03em+)
|
|
2512
|
-
|
|
2513
|
-
**Hierarchy Mapping:**
|
|
2514
|
-
/* Intentional hierarchy example */
|
|
2515
|
-
H1: font-size: 3.5rem; font-weight: 900; line-height: 1.1; letter-spacing: -0.03em;
|
|
2516
|
-
H2: font-size: 2rem; font-weight: 200; line-height: 1.2;
|
|
2517
|
-
Body: font-size: 1rem; font-weight: 400; line-height: 1.7;
|
|
2518
|
-
Caption: font-size: 0.75rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
|
|
2519
|
-
|
|
2520
|
-
## 2. Color & Theme Strategy
|
|
2521
|
-
|
|
2522
|
-
**Avoid Distributional Defaults:**
|
|
2523
|
-
- NO purple gradients or blue-purple color schemes (massively overrepresented in AI output)
|
|
2524
|
-
- NO evenly-distributed color palettes (3-4 colors used equally)
|
|
2525
|
-
- NO timid pastels or all-neutral schemes
|
|
2526
|
-
- NO #333333, #666666, #999999 grays
|
|
2527
|
-
|
|
2528
|
-
**Intentional Alternatives:**
|
|
2529
|
-
- **Commit to a Dominant Color**: Choose ONE primary brand color that appears in 60-70% of colored elements
|
|
2530
|
-
- **Sharp Accent Strategy**: Use 1-2 high-contrast accent colors sparingly (10-15% of colored elements)
|
|
2531
|
-
- **Neutrals with Personality**: Replace pure grays with warm (#3d3228, #f5f1ed) or cool (#2a2f3d, #f0f2f5) tinted neutrals
|
|
2532
|
-
|
|
2533
|
-
**Color Psychology Mapping:**
|
|
2534
|
-
- Energy/Action \u2192 Warm reds, oranges, yellows (NOT purple/blue)
|
|
2535
|
-
- Trust/Calm \u2192 Deep teals, forest greens (NOT generic blue)
|
|
2536
|
-
- Luxury/Premium \u2192 Deep burgundy, emerald, charcoal with gold accents
|
|
2537
|
-
- Playful/Creative \u2192 Unexpected combinations (coral + mint, mustard + navy)
|
|
2538
|
-
|
|
2539
|
-
**Implementation:**
|
|
2540
|
-
/* Intentional color system example */
|
|
2541
|
-
--brand-primary: #d84315; /* Dominant: Deep orange */
|
|
2542
|
-
--brand-accent: #00bfa5; /* Accent: Teal (complementary) */
|
|
2543
|
-
--neutral-dark: #2d2622; /* Warm dark brown, not #333 */
|
|
2544
|
-
--neutral-light: #faf8f6; /* Warm off-white, not pure white */
|
|
2545
|
-
--background: linear-gradient(135deg, #faf8f6 0%, #f0ebe6 100%); /* Subtle warmth */
|
|
2546
|
-
|
|
2547
|
-
## 3. Spatial Design & White Space
|
|
2548
|
-
|
|
2549
|
-
**Avoid Distributional Defaults:**
|
|
2550
|
-
- NO uniform spacing (everything 16px or 24px)
|
|
2551
|
-
- NO cramped layouts that maximize content density
|
|
2552
|
-
- NO default container widths (1200px, 1440px)
|
|
2553
|
-
|
|
2554
|
-
**Intentional Alternatives:**
|
|
2555
|
-
- **Breathing Room**: Use generous white space as a design element (80-120px vertical spacing between sections)
|
|
2556
|
-
- **Asymmetric Spacing**: Vary padding dramatically (small: 12px, medium: 48px, large: 96px)
|
|
2557
|
-
- **Content Width Strategy**:
|
|
2558
|
-
- Reading content: max 65-75 characters (600-700px)
|
|
2559
|
-
- Hero sections: asymmetric layouts, not centered blocks
|
|
2560
|
-
- Cards/components: vary sizes intentionally, not uniform grids
|
|
2561
|
-
|
|
2562
|
-
**Implementation:**
|
|
2563
|
-
/* Intentional spacing scale */
|
|
2564
|
-
--space-xs: 0.5rem; /* 8px */
|
|
2565
|
-
--space-sm: 1rem; /* 16px */
|
|
2566
|
-
--space-md: 3rem; /* 48px */
|
|
2567
|
-
--space-lg: 6rem; /* 96px */
|
|
2568
|
-
--space-xl: 10rem; /* 160px */
|
|
2569
|
-
|
|
2570
|
-
/* Use in combinations: */
|
|
2571
|
-
padding: var(--space-lg) var(--space-md); /* Not uniform padding */
|
|
2572
|
-
margin-bottom: var(--space-xl); /* Generous section breaks */
|
|
2573
|
-
|
|
2574
|
-
## 4. Backgrounds & Atmospheric Depth
|
|
2575
|
-
|
|
2576
|
-
**Avoid Distributional Defaults:**
|
|
2577
|
-
- NO solid white or light gray backgrounds
|
|
2578
|
-
- NO single-color backgrounds
|
|
2579
|
-
- NO generic gradient overlays
|
|
2580
|
-
|
|
2581
|
-
**Intentional Alternatives:**
|
|
2582
|
-
- **Layered Gradients**: Combine 2-3 subtle gradients for depth
|
|
2583
|
-
- **Geometric Patterns**: SVG patterns, mesh gradients, or subtle noise textures
|
|
2584
|
-
- **Strategic Contrast**: Alternate between light and dark sections for rhythm
|
|
2585
|
-
|
|
2586
|
-
**Implementation:**
|
|
2587
|
-
/* Intentional background example */
|
|
2588
|
-
background:
|
|
2589
|
-
radial-gradient(circle at 20% 30%, rgba(216, 67, 21, 0.08) 0%, transparent 50%),
|
|
2590
|
-
radial-gradient(circle at 80% 70%, rgba(0, 191, 165, 0.06) 0%, transparent 50%),
|
|
2591
|
-
linear-gradient(135deg, #faf8f6 0%, #f0ebe6 100%);
|
|
2592
|
-
|
|
2593
|
-
## 5. Visual Hierarchy Principles
|
|
2594
|
-
|
|
2595
|
-
**Clear Priority System:**
|
|
2596
|
-
1. **Primary Focus (1 element)**: Largest, highest contrast, most visual weight
|
|
2597
|
-
2. **Secondary Elements (2-3 elements)**: 40-60% of primary size, reduced contrast
|
|
2598
|
-
3. **Tertiary/Support (everything else)**: Minimal visual weight, muted colors
|
|
2599
|
-
|
|
2600
|
-
**Contrast Techniques:**
|
|
2601
|
-
- Size: 3x+ differences between hierarchy levels
|
|
2602
|
-
- Weight: 300+ difference in font-weight values
|
|
2603
|
-
- Color: Primary gets brand color, secondary gets neutral, tertiary gets muted
|
|
2604
|
-
- Space: Primary gets 2x+ surrounding white space vs. secondary
|
|
2605
|
-
|
|
2606
|
-
## 6. EXAMPLES - Intentional vs. Generic Design
|
|
2607
|
-
|
|
2608
|
-
### \u274C GENERIC (Distributional Convergence)
|
|
2609
|
-
|
|
2610
|
-
{
|
|
2611
|
-
"xmlStructure": "<e-flexbox configuration-id="flex1"><e-heading configuration-id="heading1"></e-heading><e-button configuration-id="button1"></e-button></e-flexbox>",
|
|
2612
|
-
"elementConfig": {
|
|
2613
|
-
"heading1": {
|
|
2614
|
-
"title": { "$$type": "string", "value": "Welcome to Our Site" }
|
|
2615
|
-
}
|
|
2616
|
-
},
|
|
2617
|
-
"stylesConfig": {
|
|
2618
|
-
"heading1": {
|
|
2619
|
-
"font-size": {
|
|
2620
|
-
"$$type": "size",
|
|
2621
|
-
"value": {
|
|
2622
|
-
"size": { "$$type": "number", "value": 24 },
|
|
2623
|
-
"unit": { "$$type": "string", "value": "px" }
|
|
2624
|
-
}
|
|
2447
|
+
// src/composition-builder/composition-builder.ts
|
|
2448
|
+
var CompositionBuilder = class _CompositionBuilder {
|
|
2449
|
+
elementConfig = {};
|
|
2450
|
+
elementStylesConfig = {};
|
|
2451
|
+
rootContainers = [];
|
|
2452
|
+
containerElements = [];
|
|
2453
|
+
api = {
|
|
2454
|
+
createElement: import_editor_elements7.createElement,
|
|
2455
|
+
getWidgetsCache: import_editor_elements7.getWidgetsCache,
|
|
2456
|
+
generateElementId: import_editor_elements7.generateElementId,
|
|
2457
|
+
getContainer: import_editor_elements7.getContainer,
|
|
2458
|
+
doUpdateElementProperty
|
|
2459
|
+
};
|
|
2460
|
+
xml;
|
|
2461
|
+
static fromXMLString(xmlString, api = {}) {
|
|
2462
|
+
const parser = new DOMParser();
|
|
2463
|
+
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
|
2464
|
+
const errorNode = xmlDoc.querySelector("parsererror");
|
|
2465
|
+
if (errorNode) {
|
|
2466
|
+
throw new Error("Failed to parse XML string: " + errorNode.textContent);
|
|
2467
|
+
}
|
|
2468
|
+
return new _CompositionBuilder({
|
|
2469
|
+
xml: xmlDoc,
|
|
2470
|
+
api
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
constructor(opts) {
|
|
2474
|
+
const { api = {}, elementConfig = {}, stylesConfig = {}, xml } = opts;
|
|
2475
|
+
this.xml = xml;
|
|
2476
|
+
Object.assign(this.api, api);
|
|
2477
|
+
this.setElementConfig(elementConfig);
|
|
2478
|
+
this.setStylesConfig(stylesConfig);
|
|
2479
|
+
}
|
|
2480
|
+
setElementConfig(config) {
|
|
2481
|
+
this.elementConfig = config;
|
|
2482
|
+
}
|
|
2483
|
+
setStylesConfig(config) {
|
|
2484
|
+
this.elementStylesConfig = config;
|
|
2485
|
+
}
|
|
2486
|
+
getXML() {
|
|
2487
|
+
return this.xml;
|
|
2488
|
+
}
|
|
2489
|
+
iterateBuild(node, containerElement, childIndex) {
|
|
2490
|
+
const elementTag = node.tagName;
|
|
2491
|
+
const isContainer = this.containerElements.includes(elementTag);
|
|
2492
|
+
const parentElType = containerElement.model.get("elType");
|
|
2493
|
+
let targetContainerId = parentElType === "e-tabs" ? containerElement.children?.[1].children?.[childIndex]?.id || containerElement.children?.[1].id : containerElement.id;
|
|
2494
|
+
if (!targetContainerId) {
|
|
2495
|
+
targetContainerId = containerElement.id;
|
|
2496
|
+
}
|
|
2497
|
+
const newElement = isContainer ? this.api.createElement({
|
|
2498
|
+
containerId: targetContainerId,
|
|
2499
|
+
model: {
|
|
2500
|
+
elType: elementTag,
|
|
2501
|
+
id: (0, import_editor_elements7.generateElementId)()
|
|
2502
|
+
},
|
|
2503
|
+
options: { useHistory: false }
|
|
2504
|
+
}) : this.api.createElement({
|
|
2505
|
+
containerId: targetContainerId,
|
|
2506
|
+
model: {
|
|
2507
|
+
elType: "widget",
|
|
2508
|
+
widgetType: elementTag,
|
|
2509
|
+
id: (0, import_editor_elements7.generateElementId)()
|
|
2625
2510
|
},
|
|
2511
|
+
options: { useHistory: false }
|
|
2512
|
+
});
|
|
2513
|
+
if (containerElement.id === "document") {
|
|
2514
|
+
this.rootContainers.push(newElement);
|
|
2515
|
+
}
|
|
2516
|
+
node.setAttribute("id", newElement.id);
|
|
2517
|
+
let currentChild = 0;
|
|
2518
|
+
for (const childNode of Array.from(node.children)) {
|
|
2519
|
+
this.iterateBuild(childNode, newElement, currentChild);
|
|
2520
|
+
currentChild++;
|
|
2626
2521
|
}
|
|
2627
2522
|
}
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
},
|
|
2639
|
-
"hero-subtitle": {
|
|
2640
|
-
"content": { "$$type": "string", "value": "Built for teams who refuse to compromise on quality" }
|
|
2523
|
+
findSchemaForNode(node) {
|
|
2524
|
+
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
2525
|
+
const widgetType = node.tagName;
|
|
2526
|
+
const widgetData = widgetsCache[widgetType]?.atomic_props_schema;
|
|
2527
|
+
return widgetData || null;
|
|
2528
|
+
}
|
|
2529
|
+
matchNodeByConfigId(configId) {
|
|
2530
|
+
const node = this.xml.querySelector(`[configuration-id="${configId}"]`);
|
|
2531
|
+
if (!node) {
|
|
2532
|
+
throw new Error(`Configuration id "${configId}" does not have target node.`);
|
|
2641
2533
|
}
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
"
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2534
|
+
const id = node.getAttribute("id");
|
|
2535
|
+
if (!id) {
|
|
2536
|
+
throw new Error(`Node with configuration id "${configId}" does not have element id.`);
|
|
2537
|
+
}
|
|
2538
|
+
const element = this.api.getContainer(id);
|
|
2539
|
+
if (!element) {
|
|
2540
|
+
throw new Error(`Element with id "${id}" not found but should exist.`);
|
|
2541
|
+
}
|
|
2542
|
+
return {
|
|
2543
|
+
element,
|
|
2544
|
+
node
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
applyStyles() {
|
|
2548
|
+
const errors = [];
|
|
2549
|
+
const invalidStyles = {};
|
|
2550
|
+
const validStylesPropValues = {};
|
|
2551
|
+
for (const [styleId, styleConfig] of Object.entries(this.elementStylesConfig)) {
|
|
2552
|
+
const { element, node } = this.matchNodeByConfigId(styleId);
|
|
2553
|
+
for (const [styleName, stylePropValue] of Object.entries(styleConfig)) {
|
|
2554
|
+
const { valid, errors: validationErrors } = validateInput.validateStyles({
|
|
2555
|
+
[styleName]: stylePropValue
|
|
2556
|
+
});
|
|
2557
|
+
if (!valid) {
|
|
2558
|
+
if (styleConfig.$intention) {
|
|
2559
|
+
invalidStyles[element.id] = invalidStyles[element.id] || [];
|
|
2560
|
+
invalidStyles[element.id].push(styleName);
|
|
2561
|
+
}
|
|
2562
|
+
errors.push(...validationErrors || []);
|
|
2563
|
+
} else {
|
|
2564
|
+
validStylesPropValues[styleName] = stylePropValue;
|
|
2565
|
+
}
|
|
2566
|
+
this.api.doUpdateElementProperty({
|
|
2567
|
+
elementId: element.id,
|
|
2568
|
+
propertyName: "_styles",
|
|
2569
|
+
propertyValue: validStylesPropValues,
|
|
2570
|
+
elementType: node.tagName
|
|
2571
|
+
});
|
|
2652
2572
|
}
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2573
|
+
}
|
|
2574
|
+
return {
|
|
2575
|
+
errors,
|
|
2576
|
+
invalidStyles
|
|
2577
|
+
};
|
|
2578
|
+
}
|
|
2579
|
+
applyConfigs() {
|
|
2580
|
+
const errors = [];
|
|
2581
|
+
for (const [configId, config] of Object.entries(this.elementConfig)) {
|
|
2582
|
+
const { element, node } = this.matchNodeByConfigId(configId);
|
|
2583
|
+
const propSchema = this.findSchemaForNode(node);
|
|
2584
|
+
const result = validateInput.validateProps(propSchema, config);
|
|
2585
|
+
if (!result.valid && result.errors?.length) {
|
|
2586
|
+
errors.push(...result.errors);
|
|
2587
|
+
} else {
|
|
2588
|
+
for (const [propertyName, propertyValue] of Object.entries(config)) {
|
|
2589
|
+
try {
|
|
2590
|
+
this.api.doUpdateElementProperty({
|
|
2591
|
+
elementId: element.id,
|
|
2592
|
+
propertyName,
|
|
2593
|
+
propertyValue,
|
|
2594
|
+
elementType: node.tagName
|
|
2595
|
+
});
|
|
2596
|
+
} catch (error) {
|
|
2597
|
+
errors.push(error.message);
|
|
2660
2598
|
}
|
|
2661
|
-
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2662
2601
|
}
|
|
2602
|
+
return errors;
|
|
2663
2603
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
**Color Constraints:**
|
|
2690
|
-
- NEVER use purple gradients or blue-purple color schemes
|
|
2691
|
-
- NEVER use pure grays (#333, #666, #999) - use tinted neutrals instead
|
|
2692
|
-
- NEVER distribute colors evenly - commit to ONE dominant color
|
|
2693
|
-
- NEVER use more than 3 core colors (1 dominant, 1-2 accents)
|
|
2694
|
-
|
|
2695
|
-
**Spacing Constraints:**
|
|
2696
|
-
- NEVER use uniform spacing across all elements
|
|
2697
|
-
- NEVER use section padding less than 4rem (64px) for hero/major sections
|
|
2698
|
-
- NEVER center everything - use asymmetric layouts for visual interest
|
|
2699
|
-
|
|
2700
|
-
**Background Constraints:**
|
|
2701
|
-
- NEVER use solid white (#ffffff) or light gray (#f5f5f5) backgrounds without texture/gradients
|
|
2702
|
-
- ALWAYS layer at least 2 gradient or color elements for atmospheric depth
|
|
2703
|
-
|
|
2704
|
-
# Parameters
|
|
2705
|
-
All parameters are MANDATORY.
|
|
2706
|
-
- xmlStructure
|
|
2707
|
-
- elementConfig
|
|
2708
|
-
- stylesConfig
|
|
2709
|
-
|
|
2710
|
-
If unsure about the configuration of a specific property, read the schema resources carefully.
|
|
2711
|
-
|
|
2712
|
-
# About our widgets
|
|
2713
|
-
Most widgets are self-explanatory by their name. Here is some additional information.
|
|
2714
|
-
Check for available llm_guidance property in the widget's schema.
|
|
2715
|
-
SVG elements are bound to internal content upload. Avoid usage, unless you have tools to upload SVG content.
|
|
2716
|
-
When working with containers, do not forget to apply style schema for controlling the layout.
|
|
2717
|
-
|
|
2604
|
+
build(rootContainer) {
|
|
2605
|
+
const widgetsCache = this.api.getWidgetsCache() || {};
|
|
2606
|
+
const CONTAINER_ELEMENTS = Object.values(widgetsCache).filter((widget) => widget.meta?.is_container).map((widget) => widget.elType).filter((x) => typeof x === "string");
|
|
2607
|
+
this.containerElements = CONTAINER_ELEMENTS;
|
|
2608
|
+
new Set(this.xml.querySelectorAll("*")).forEach((node) => {
|
|
2609
|
+
if (!widgetsCache[node.tagName]) {
|
|
2610
|
+
throw new Error(`Unknown widget type: ${node.tagName}`);
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
const children = Array.from(this.xml.children);
|
|
2614
|
+
let currentChild = 0;
|
|
2615
|
+
for (const childNode of children) {
|
|
2616
|
+
this.iterateBuild(childNode, rootContainer, currentChild);
|
|
2617
|
+
currentChild++;
|
|
2618
|
+
}
|
|
2619
|
+
const { errors: styleErrors, invalidStyles } = this.applyStyles();
|
|
2620
|
+
const configErrors = this.applyConfigs();
|
|
2621
|
+
return {
|
|
2622
|
+
configErrors,
|
|
2623
|
+
styleErrors,
|
|
2624
|
+
invalidStyles,
|
|
2625
|
+
rootContainers: [...this.rootContainers]
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
};
|
|
2718
2629
|
|
|
2630
|
+
// src/mcp/tools/build-composition/prompt.ts
|
|
2631
|
+
var import_editor_mcp2 = require("@elementor/editor-mcp");
|
|
2632
|
+
var generatePrompt = () => {
|
|
2633
|
+
const buildCompositionsToolPrompt = (0, import_editor_mcp2.toolPrompts)("build-compositions");
|
|
2634
|
+
buildCompositionsToolPrompt.description(`
|
|
2635
|
+
# REQUIRED RESOURCES (Read before use)
|
|
2636
|
+
1. [${WIDGET_SCHEMA_URI}] - Widget types, configuration schemas, and PropType definitions
|
|
2637
|
+
2. [${STYLE_SCHEMA_URI}] - Common styles schema shared by all widgets
|
|
2638
|
+
3. [elementor://global-classes] - Existing global classes (check FIRST to reuse)
|
|
2639
|
+
|
|
2640
|
+
# THREE-PHASE WORKFLOW (MANDATORY)
|
|
2641
|
+
|
|
2642
|
+
## Phase 1: Create Global Classes
|
|
2643
|
+
1. Analyze requirements \u2192 identify reusable patterns (typography, colors, spacing)
|
|
2644
|
+
2. Check [elementor://global-classes] for existing classes
|
|
2645
|
+
3. Use "create-global-class" tool for NEW reusable styles BEFORE building
|
|
2646
|
+
|
|
2647
|
+
## Phase 2: Build Composition (THIS TOOL)
|
|
2648
|
+
4. Build valid XML with minimal inline styles (layout/positioning only)
|
|
2649
|
+
5. Avoid duplicating styles that should be global classes
|
|
2650
|
+
|
|
2651
|
+
## Phase 3: Apply Classes
|
|
2652
|
+
6. Use "apply-global-class" tool to apply global classes to elements
|
|
2653
|
+
|
|
2654
|
+
# CORE INSTRUCTIONS
|
|
2655
|
+
|
|
2656
|
+
**Structure:**
|
|
2657
|
+
- Build valid XML using allowed widget tags (e.g., \`<e-button configuration-id="btn1"></e-button>\`)
|
|
2658
|
+
- Containers only: "e-flexbox", "e-div-block", "e-tabs"
|
|
2659
|
+
- Every element MUST have unique "configuration-id" attribute
|
|
2660
|
+
- No attributes, classes, IDs, or text nodes in XML
|
|
2661
|
+
|
|
2662
|
+
**Configuration:**
|
|
2663
|
+
- Map each configuration-id to elementConfig (widget props) and stylesConfig (styles)
|
|
2664
|
+
- Follow exact PropType schemas from resources above
|
|
2665
|
+
- All PropValues need \`$$type\` property matching schema
|
|
2666
|
+
- Keep stylesConfig MINIMAL - layout only, NOT reusable styles
|
|
2667
|
+
|
|
2668
|
+
**Validation:**
|
|
2669
|
+
- Parse XML before submission
|
|
2670
|
+
- Match all PropValues to schema (\`$$type\` required)
|
|
2671
|
+
- NO LINKS in any configuration
|
|
2672
|
+
- Retry on errors up to 10x, reading error messages carefully
|
|
2673
|
+
|
|
2674
|
+
# DESIGN QUALITY: AVOID AI SLOP
|
|
2675
|
+
|
|
2676
|
+
**Problem:** LLMs default to generic patterns (purple gradients, #333 grays, 24px headings, uniform spacing)
|
|
2677
|
+
**Solution:** Make intentional, distinctive choices. When unsure, choose bold over safe.
|
|
2678
|
+
|
|
2679
|
+
## Typography Rules
|
|
2680
|
+
\u274C AVOID: Inter/Roboto/Arial, small ratios (1.5x), medium weights (500-700)
|
|
2681
|
+
\u2705 USE: 3x+ size ratios, extreme weight contrasts (100/200 vs 800/900), tight headlines (1.1 line-height)
|
|
2682
|
+
|
|
2683
|
+
## Color Rules
|
|
2684
|
+
\u274C AVOID: Purple gradients, pure grays (#333/#666/#999), even distribution
|
|
2685
|
+
\u2705 USE: ONE dominant color (60-70%), 1-2 accent colors (10-15%), tinted neutrals (warm/cool grays)
|
|
2686
|
+
|
|
2687
|
+
## Spacing Rules
|
|
2688
|
+
\u274C AVOID: Uniform spacing (all 16px/24px), cramped layouts, centered everything
|
|
2689
|
+
\u2705 USE: Generous spacing (80-120px sections), dramatic variation (12px/48px/96px), asymmetric layouts
|
|
2690
|
+
|
|
2691
|
+
## Background Rules
|
|
2692
|
+
\u274C AVOID: Solid white/gray, single colors
|
|
2693
|
+
\u2705 USE: Layered gradients (2-3 layers), subtle patterns, alternating light/dark sections
|
|
2694
|
+
|
|
2695
|
+
## Visual Hierarchy
|
|
2696
|
+
1. **Primary** (1 element): Largest, highest contrast, most space
|
|
2697
|
+
2. **Secondary** (2-3 elements): 40-60% of primary size
|
|
2698
|
+
3. **Tertiary** (rest): Minimal weight, muted
|
|
2699
|
+
|
|
2700
|
+
**Contrast techniques:** 3x size differences, 300+ weight differences, color hierarchy (brand \u2192 neutral \u2192 muted)
|
|
2701
|
+
|
|
2702
|
+
# DESIGN CONSTRAINTS (NEVER VIOLATE)
|
|
2703
|
+
|
|
2704
|
+
**Typography:**
|
|
2705
|
+
- NEVER use Inter, Roboto, Arial, Helvetica as primary display fonts
|
|
2706
|
+
- NEVER use font-size ratios < 2.5x between headlines and body
|
|
2707
|
+
- NEVER use font-weight 500-700 for headlines (go lighter or heavier)
|
|
2708
|
+
|
|
2709
|
+
**Color:**
|
|
2710
|
+
- PREFER not to use pure grays - use tinted neutrals (#2d2622, #faf8f6, not #333/#f5f5f5)
|
|
2711
|
+
- NEVER distribute colors evenly - commit to ONE dominant
|
|
2712
|
+
- NEVER use more than 3 core colors - except for info/alert/badges
|
|
2713
|
+
|
|
2714
|
+
**Spacing:**
|
|
2715
|
+
- NEVER use uniform spacing
|
|
2716
|
+
- NEVER use < 4rem (64px) padding for major sections
|
|
2717
|
+
- NEVER center everything
|
|
2718
|
+
- PRIORITIZE rem based values over pixel based
|
|
2719
|
+
|
|
2720
|
+
**Background:**
|
|
2721
|
+
- NEVER use solid #ffffff or #f5f5f5 without texture/gradients
|
|
2722
|
+
- ALWAYS layer 2+ gradient/color elements
|
|
2723
|
+
|
|
2724
|
+
# WIDGET NOTES
|
|
2725
|
+
- Check \`llm_guidance\` property in widget schemas for context
|
|
2726
|
+
- Avoid SVG widgets (require content upload tools) - when must, prior to execution ensure assets uploaded
|
|
2727
|
+
- Apply style schema to containers for layout control
|
|
2728
|
+
|
|
2729
|
+
# PARAMETERS (ALL MANDATORY)
|
|
2730
|
+
- **xmlStructure**: Valid XML with configuration-id attributes
|
|
2731
|
+
- **elementConfig**: Record of configuration-id \u2192 widget PropValues
|
|
2732
|
+
- **stylesConfig**: Record of configuration-id \u2192 style PropValues (layout only)
|
|
2719
2733
|
`);
|
|
2720
2734
|
buildCompositionsToolPrompt.example(`
|
|
2721
2735
|
A Heading and a button inside a flexbox
|
|
@@ -2762,7 +2776,12 @@ A Heading and a button inside a flexbox
|
|
|
2762
2776
|
You should use these IDs as reference for further configuration, styling or changing elements later on.`
|
|
2763
2777
|
);
|
|
2764
2778
|
buildCompositionsToolPrompt.instruction(
|
|
2765
|
-
|
|
2779
|
+
`**CRITICAL WORKFLOW REMINDER**:
|
|
2780
|
+
1. FIRST: Create reusable global classes for typography, colors, spacing patterns using "create-global-class" tool
|
|
2781
|
+
2. SECOND: Use THIS tool with minimal inline styles (only layout & unique properties)
|
|
2782
|
+
3. THIRD: Apply global classes to elements using "apply-global-class" tool
|
|
2783
|
+
|
|
2784
|
+
This ensures maximum reusability and consistency across your design system. ALWAYS check [elementor://global-classes] for existing classes before creating new ones.`
|
|
2766
2785
|
);
|
|
2767
2786
|
return buildCompositionsToolPrompt.prompt();
|
|
2768
2787
|
};
|
|
@@ -2792,8 +2811,7 @@ var outputSchema = {
|
|
|
2792
2811
|
errors: import_schema.z.string().describe("Error message if the composition building failed").optional(),
|
|
2793
2812
|
xmlStructure: import_schema.z.string().describe(
|
|
2794
2813
|
"The built XML structure as a string. Must use this XML after completion of building the composition, it contains real IDs."
|
|
2795
|
-
).optional()
|
|
2796
|
-
llmInstructions: import_schema.z.string().describe("Instructions for what to do next, Important to follow these instructions!")
|
|
2814
|
+
).optional()
|
|
2797
2815
|
};
|
|
2798
2816
|
|
|
2799
2817
|
// src/mcp/tools/build-composition/tool.ts
|
|
@@ -2805,120 +2823,60 @@ var initBuildCompositionsTool = (reg) => {
|
|
|
2805
2823
|
schema: inputSchema,
|
|
2806
2824
|
requiredResources: [
|
|
2807
2825
|
{ description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
|
|
2808
|
-
{ description: "Global Classes", uri: "elementor://global-classes" },
|
|
2809
2826
|
{ description: "Styles schema", uri: STYLE_SCHEMA_URI },
|
|
2827
|
+
{ description: "Global Classes", uri: "elementor://global-classes" },
|
|
2810
2828
|
{ description: "Global Variables", uri: "elementor://global-variables" },
|
|
2811
2829
|
{ description: "Styles best practices", uri: BEST_PRACTICES_URI }
|
|
2812
2830
|
],
|
|
2813
2831
|
outputSchema,
|
|
2814
2832
|
modelPreferences: {
|
|
2815
|
-
hints: [{ name: "claude-sonnet-4-5" }]
|
|
2816
|
-
intelligencePriority: 0.95,
|
|
2817
|
-
speedPriority: 0.5
|
|
2833
|
+
hints: [{ name: "claude-sonnet-4-5" }]
|
|
2818
2834
|
},
|
|
2819
2835
|
handler: async (params) => {
|
|
2820
|
-
let xml = null;
|
|
2821
2836
|
const { xmlStructure, elementConfig, stylesConfig } = params;
|
|
2837
|
+
let generatedXML = "";
|
|
2822
2838
|
const errors = [];
|
|
2823
|
-
const softErrors = [];
|
|
2824
2839
|
const rootContainers = [];
|
|
2825
|
-
const
|
|
2826
|
-
const documentContainer = (0, import_editor_elements7.getContainer)("document");
|
|
2840
|
+
const documentContainer = (0, import_editor_elements8.getContainer)("document");
|
|
2827
2841
|
try {
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2842
|
+
const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
|
|
2843
|
+
createElement: import_editor_elements8.createElement,
|
|
2844
|
+
getWidgetsCache: import_editor_elements8.getWidgetsCache
|
|
2845
|
+
});
|
|
2846
|
+
compositionBuilder.setElementConfig(elementConfig);
|
|
2847
|
+
compositionBuilder.setStylesConfig(stylesConfig);
|
|
2848
|
+
const {
|
|
2849
|
+
configErrors,
|
|
2850
|
+
invalidStyles,
|
|
2851
|
+
rootContainers: generatedRootContainers
|
|
2852
|
+
} = compositionBuilder.build(documentContainer);
|
|
2853
|
+
generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
|
|
2854
|
+
if (configErrors.length) {
|
|
2855
|
+
errors.push(...configErrors.map((e) => new Error(e)));
|
|
2856
|
+
throw new Error("Configuration errors occurred during composition building.");
|
|
2833
2857
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
const
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
}
|
|
2847
|
-
const newElement = isContainer ? (0, import_editor_elements7.createElement)({
|
|
2848
|
-
containerId: targetContainerId,
|
|
2849
|
-
model: {
|
|
2850
|
-
elType: elementTag,
|
|
2851
|
-
id: (0, import_editor_elements7.generateElementId)()
|
|
2852
|
-
},
|
|
2853
|
-
options: { useHistory: false }
|
|
2854
|
-
}) : (0, import_editor_elements7.createElement)({
|
|
2855
|
-
containerId: targetContainerId,
|
|
2856
|
-
model: {
|
|
2857
|
-
elType: "widget",
|
|
2858
|
-
widgetType: elementTag,
|
|
2859
|
-
id: (0, import_editor_elements7.generateElementId)()
|
|
2858
|
+
rootContainers.push(...generatedRootContainers);
|
|
2859
|
+
Object.entries(invalidStyles).forEach(([elementId, rawCssRules]) => {
|
|
2860
|
+
const customCss = {
|
|
2861
|
+
value: rawCssRules.join(";\n")
|
|
2862
|
+
};
|
|
2863
|
+
doUpdateElementProperty({
|
|
2864
|
+
elementId,
|
|
2865
|
+
propertyName: "_styles",
|
|
2866
|
+
propertyValue: {
|
|
2867
|
+
_styles: {
|
|
2868
|
+
custom_css: customCss
|
|
2869
|
+
}
|
|
2860
2870
|
},
|
|
2861
|
-
|
|
2871
|
+
elementType: "widget"
|
|
2862
2872
|
});
|
|
2863
|
-
|
|
2864
|
-
rootContainers.push(newElement);
|
|
2865
|
-
}
|
|
2866
|
-
node.setAttribute("id", newElement.id);
|
|
2867
|
-
const configId = node.getAttribute("configuration-id") || "";
|
|
2868
|
-
try {
|
|
2869
|
-
const configObject = elementConfig[configId] || {};
|
|
2870
|
-
const styleObject = stylesConfig[configId] || {};
|
|
2871
|
-
const { errors: propsValidationErrors } = validateInput.validatePropSchema(
|
|
2872
|
-
elementTag,
|
|
2873
|
-
configObject
|
|
2874
|
-
);
|
|
2875
|
-
errors.push(...(propsValidationErrors || []).map((msg) => new Error(msg)));
|
|
2876
|
-
const { errors: stylesValidationErrors } = validateInput.validateStyles(styleObject);
|
|
2877
|
-
errors.push(...(stylesValidationErrors || []).map((msg) => new Error(msg)));
|
|
2878
|
-
if (propsValidationErrors?.length || stylesValidationErrors?.length) {
|
|
2879
|
-
return;
|
|
2880
|
-
}
|
|
2881
|
-
configObject._styles = styleObject || {};
|
|
2882
|
-
for (const [propertyName, propertyValue] of Object.entries(configObject)) {
|
|
2883
|
-
try {
|
|
2884
|
-
doUpdateElementProperty({
|
|
2885
|
-
elementId: newElement.id,
|
|
2886
|
-
propertyName,
|
|
2887
|
-
propertyValue,
|
|
2888
|
-
elementType: elementTag
|
|
2889
|
-
});
|
|
2890
|
-
} catch (error) {
|
|
2891
|
-
softErrors.push(error);
|
|
2892
|
-
}
|
|
2893
|
-
}
|
|
2894
|
-
if (isContainer) {
|
|
2895
|
-
let currentChild2 = 0;
|
|
2896
|
-
for (const child of node.children) {
|
|
2897
|
-
iterate(child, newElement, currentChild2);
|
|
2898
|
-
currentChild2++;
|
|
2899
|
-
}
|
|
2900
|
-
} else {
|
|
2901
|
-
node.innerHTML = "";
|
|
2902
|
-
node.removeAttribute("configuration");
|
|
2903
|
-
}
|
|
2904
|
-
} finally {
|
|
2905
|
-
}
|
|
2906
|
-
};
|
|
2907
|
-
let currentChild = 0;
|
|
2908
|
-
for await (const childNode of children) {
|
|
2909
|
-
await iterate(childNode, documentContainer, currentChild);
|
|
2910
|
-
currentChild++;
|
|
2911
|
-
try {
|
|
2912
|
-
} catch (error) {
|
|
2913
|
-
errors.push(error);
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2873
|
+
});
|
|
2916
2874
|
} catch (error) {
|
|
2917
2875
|
errors.push(error);
|
|
2918
2876
|
}
|
|
2919
2877
|
if (errors.length) {
|
|
2920
2878
|
rootContainers.forEach((rootContainer) => {
|
|
2921
|
-
(0,
|
|
2879
|
+
(0, import_editor_elements8.deleteElement)({
|
|
2922
2880
|
elementId: rootContainer.id,
|
|
2923
2881
|
options: { useHistory: false }
|
|
2924
2882
|
});
|
|
@@ -2952,20 +2910,19 @@ ${errorMessages.join(
|
|
|
2952
2910
|
Now that you have these errors, fix them and try again. Errors regarding configuration objects, please check against the PropType schemas`;
|
|
2953
2911
|
throw new Error(errorText);
|
|
2954
2912
|
}
|
|
2955
|
-
if (!xml) {
|
|
2956
|
-
throw new Error("XML structure is null after parsing.");
|
|
2957
|
-
}
|
|
2958
2913
|
return {
|
|
2959
|
-
xmlStructure:
|
|
2914
|
+
xmlStructure: generatedXML,
|
|
2960
2915
|
errors: errors?.length ? errors.map((e) => typeof e === "string" ? e : e.message).join("\n\n") : void 0,
|
|
2961
|
-
|
|
2916
|
+
llm_instructions: `The composition was built successfully with element IDs embedded in the XML.
|
|
2962
2917
|
|
|
2963
|
-
|
|
2918
|
+
**CRITICAL NEXT STEPS** (Follow in order):
|
|
2919
|
+
1. **Apply Global Classes**: Use "apply-global-class" tool to apply the global classes you created BEFORE building this composition
|
|
2920
|
+
- Check the created element IDs in the returned XML
|
|
2921
|
+
- Apply semantic classes (heading-primary, button-cta, etc.) to appropriate elements
|
|
2964
2922
|
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
- Use "configure-element" tool to further configure elements as needed, including styles.
|
|
2923
|
+
2. **Fine-tune if needed**: Use "configure-element" tool only for element-specific adjustments that don't warrant global classes
|
|
2924
|
+
|
|
2925
|
+
Remember: Global classes ensure design consistency and reusability. Don't skip applying them!
|
|
2969
2926
|
`
|
|
2970
2927
|
};
|
|
2971
2928
|
}
|
|
@@ -3191,7 +3148,7 @@ Check the styles schema at the resource [${STYLE_SCHEMA_URI.replace(
|
|
|
3191
3148
|
}
|
|
3192
3149
|
|
|
3193
3150
|
// src/mcp/tools/get-element-config/tool.ts
|
|
3194
|
-
var
|
|
3151
|
+
var import_editor_elements9 = require("@elementor/editor-elements");
|
|
3195
3152
|
var import_editor_props7 = require("@elementor/editor-props");
|
|
3196
3153
|
var import_schema5 = require("@elementor/schema");
|
|
3197
3154
|
var schema = {
|
|
@@ -3230,12 +3187,12 @@ var initGetElementConfigTool = (reg) => {
|
|
|
3230
3187
|
speedPriority: 0.9
|
|
3231
3188
|
},
|
|
3232
3189
|
handler: async ({ elementId }) => {
|
|
3233
|
-
const element = (0,
|
|
3190
|
+
const element = (0, import_editor_elements9.getContainer)(elementId);
|
|
3234
3191
|
if (!element) {
|
|
3235
3192
|
throw new Error(`Element with ID ${elementId} not found.`);
|
|
3236
3193
|
}
|
|
3237
3194
|
const elementRawSettings = element.settings;
|
|
3238
|
-
const propSchema = (0,
|
|
3195
|
+
const propSchema = (0, import_editor_elements9.getWidgetsCache)()?.[element.model.get("widgetType") || element.model.get("elType") || ""]?.atomic_props_schema;
|
|
3239
3196
|
if (!elementRawSettings || !propSchema) {
|
|
3240
3197
|
throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
|
|
3241
3198
|
}
|
|
@@ -3244,7 +3201,7 @@ var initGetElementConfigTool = (reg) => {
|
|
|
3244
3201
|
import_editor_props7.Schema.configurableKeys(propSchema).forEach((key) => {
|
|
3245
3202
|
propValues[key] = structuredClone(elementRawSettings.get(key));
|
|
3246
3203
|
});
|
|
3247
|
-
const elementStyles = (0,
|
|
3204
|
+
const elementStyles = (0, import_editor_elements9.getElementStyles)(elementId) || {};
|
|
3248
3205
|
const localStyle = Object.values(elementStyles).find((style) => style.label === "local");
|
|
3249
3206
|
if (localStyle) {
|
|
3250
3207
|
const defaultVariant = localStyle.variants.find(
|
|
@@ -3291,127 +3248,118 @@ var initCanvasMcp = (reg) => {
|
|
|
3291
3248
|
|
|
3292
3249
|
// src/mcp/mcp-description.ts
|
|
3293
3250
|
var ELEMENT_SCHEMA_URI = WIDGET_SCHEMA_URI.replace("{widgetType}", "element-schema");
|
|
3294
|
-
var mcpDescription = `Canvas MCP
|
|
3295
|
-
This MCP enables
|
|
3251
|
+
var mcpDescription = `Elementor Canvas MCP
|
|
3252
|
+
This MCP enables creation, configuration, and styling of elements on the Elementor canvas using the build_composition tool.
|
|
3296
3253
|
|
|
3297
|
-
#
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3254
|
+
# Core Concepts
|
|
3255
|
+
|
|
3256
|
+
## PropValues Structure
|
|
3257
|
+
All data in Elementor uses PropValues - a typed wrapper for values:
|
|
3258
|
+
\`\`\`json
|
|
3259
|
+
{
|
|
3260
|
+
"$$type": "the-prop-type-schema-kind",
|
|
3261
|
+
"value": "the-actual-value-as-defined-for-the-propType"
|
|
3262
|
+
}
|
|
3263
|
+
\`\`\`
|
|
3264
|
+
The \`$$type\` defines how Elementor interprets the value. Providing the correct \`$$type\` is critical - incorrect types will be rejected.
|
|
3265
|
+
|
|
3266
|
+
## Design System Resources
|
|
3267
|
+
- **Global Variables**: Reusable colors, sizes, and fonts (\`elementor://global-variables\`)
|
|
3268
|
+
- **Global Classes**: Reusable style sets that can be applied to elements (\`elementor://global-classes\`)
|
|
3269
|
+
- **Widget Schemas**: Configuration options for each widget type (\`${WIDGET_SCHEMA_URI}\`)
|
|
3270
|
+
- **Style Schema**: Common styles shared across all widgets and containers (\`${STYLE_SCHEMA_URI}\`)
|
|
3271
|
+
|
|
3272
|
+
# Building Compositions with build_composition
|
|
3273
|
+
|
|
3274
|
+
The \`build_composition\` tool is the primary way to create elements. It accepts structure (XML), configuration, and styling in a single operation.
|
|
3275
|
+
|
|
3276
|
+
## Complete Workflow
|
|
3277
|
+
|
|
3278
|
+
### 1. Parse User Requirements
|
|
3279
|
+
Understand what needs to be built: structure, content, and styling.
|
|
3280
|
+
|
|
3281
|
+
### 2. Check Global Resources FIRST
|
|
3282
|
+
Always check existing resources before building:
|
|
3283
|
+
- List \`elementor://global-variables\` for available variables (colors, sizes, fonts)
|
|
3284
|
+
- List \`elementor://global-classes\` for available style sets
|
|
3285
|
+
- **Always prefer using existing global resources over creating inline styles**
|
|
3286
|
+
|
|
3287
|
+
### 3. Retrieve Widget Schemas
|
|
3288
|
+
For each widget you'll use:
|
|
3289
|
+
- List \`${WIDGET_SCHEMA_URI}\` to see available widgets
|
|
3290
|
+
- Retrieve configuration schema from \`${ELEMENT_SCHEMA_URI}\` for each widget
|
|
3291
|
+
- Check the \`llm_guidance\` property to understand if a widget is a container (can have children)
|
|
3292
|
+
|
|
3293
|
+
### 4. Build XML Structure
|
|
3294
|
+
Create valid XML with configuration-ids:
|
|
3295
|
+
- Each element must have a unique \`configuration-id\` attribute
|
|
3296
|
+
- No text nodes, classes, or IDs in XML - structure only
|
|
3297
|
+
- Example:
|
|
3298
|
+
\`\`\`xml
|
|
3299
|
+
<e-container configuration-id="container-1">
|
|
3300
|
+
<e-heading configuration-id="heading-1" />
|
|
3301
|
+
<e-text configuration-id="text-1" />
|
|
3302
|
+
</e-container>
|
|
3303
|
+
\`\`\`
|
|
3301
3304
|
|
|
3302
|
-
|
|
3305
|
+
### 5. Create elementConfig
|
|
3306
|
+
Map each configuration-id to its widget properties using PropValues:
|
|
3307
|
+
- Use correct \`$$type\` matching the widget's schema
|
|
3308
|
+
- Use global variables in PropValues where applicable
|
|
3309
|
+
- Example:
|
|
3303
3310
|
\`\`\`json
|
|
3304
3311
|
{
|
|
3305
|
-
|
|
3306
|
-
|
|
3312
|
+
"heading-1": {
|
|
3313
|
+
"text": { "$$type": "string", "value": "Welcome" },
|
|
3314
|
+
"tag": { "$$type": "string", "value": "h1" }
|
|
3315
|
+
}
|
|
3307
3316
|
}
|
|
3308
3317
|
\`\`\`
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
The style schema also defines the global classes.
|
|
3315
|
-
|
|
3316
|
-
To understand the configuration options for an element, refer to the PropType schema for that specific element. For example: "e-heading" configuration schema is available at the resource [${WIDGET_SCHEMA_URI}/e-heading]
|
|
3317
|
-
|
|
3318
|
-
# Modifying elements and styles
|
|
3319
|
-
When configuring an element, elementor does a MERGE PROPERTIES operation, which means that only the properties you provide will be updated, and the rest will remain as is.
|
|
3320
|
-
For deleting a property, the value must be set to null, instead of a PropValue. When adding a configuration, no need to provide the full configuration, only the properties you want to add or update.
|
|
3321
|
-
The same rule applies to styles as well and modifications to global classes.
|
|
3322
|
-
|
|
3323
|
-
# Building full compositions
|
|
3324
|
-
The "build-composition" tool allows creating multiple elements in a single operation.
|
|
3325
|
-
The tool accepts both the structure, the styling and the configuration of each element to be created.
|
|
3326
|
-
|
|
3327
|
-
- First step: Retreive the available widgets by listing the [${WIDGET_SCHEMA_URI}] dynamic resource.
|
|
3328
|
-
- Second step: decide which elements to create, and their configuration and styles.
|
|
3329
|
-
Retrieve the used elements configuration schema from the resource [${ELEMENT_SCHEMA_URI}]
|
|
3330
|
-
- Third step: define the styles for each element, using the common styles schema from the resource [${STYLE_SCHEMA_URI}]. List the resource to see all available style properties.
|
|
3331
|
-
|
|
3332
|
-
## Workflow for build-compositions tool
|
|
3333
|
-
1. **Parse user requirements** - Undestand what needs to be built (structure, content, styling).
|
|
3334
|
-
2. ** Check global resources FIRST** - Always check before building:
|
|
3335
|
-
- List \`elementor://global-classes\` to see if the needed global classes already exist.
|
|
3336
|
-
- List \`elementor://global-variables\` to see if the needed global variables already exist.
|
|
3337
|
-
- **Preference**: Use existing global classes and variables over creating new inline styles.
|
|
3338
|
-
3. **Retreive widget schemas** - For each widget you will use:
|
|
3339
|
-
- List [${WIDGET_SCHEMA_URI}] to see available widgets.
|
|
3340
|
-
- Retrieve configuration schema from [${ELEMENT_SCHEMA_URI}] for each widget to understand required properties.
|
|
3341
|
-
- Understand if a widget is a container (can have children) by checking the "llm_guidance" property in the widget's schema.
|
|
3342
|
-
4. **Build XML structure** - Create valid XML width configuration-ids:
|
|
3343
|
-
- Every element has unique configuration-id attribute
|
|
3344
|
-
- No text nodes, classes, IDs in XML.
|
|
3345
|
-
|
|
3346
|
-
5. **Create elementConfig** - For each configuration-id:
|
|
3347
|
-
- Use PropValues with corrent \`$$type\` matching the schema.
|
|
3348
|
-
- **Use global variables** in PropValues where applicable
|
|
3349
|
-
|
|
3350
|
-
6. **Create stylesConfig** - For each configuration-id:
|
|
3351
|
-
- Use style schema PropValues from [${STYLE_SCHEMA_URI}]
|
|
3352
|
-
- **Use global variables** for colors, sizes, fonts where applicable
|
|
3353
|
-
- **Important**: Global classes are applied AFTER building the composition. Once built, apply classes using the "apply-global-class" tool - after completion of building the composition.
|
|
3354
|
-
|
|
3355
|
-
7. **Validate** - Ensure:
|
|
3356
|
-
- XML structure is valid
|
|
3357
|
-
- All PropValues have corrent \`$$type\`
|
|
3358
|
-
- After building a composition, you can retreive each element configuration using the "get-element-configuration-values" tool to verify correctness, using the IDs returned from the build-composition tool.
|
|
3359
|
-
|
|
3360
|
-
## Key relationships:
|
|
3361
|
-
- **XML structure**: Defines the hierarchy of elements/widgets
|
|
3362
|
-
- **elementConfig**: Maps configuration-id to widget PropValues
|
|
3363
|
-
- **stylesConfig**: Maps configuration-id to style PropValues
|
|
3364
|
-
|
|
3365
|
-
## Using global variables example:
|
|
3366
|
-
Existing global variables can be used during composition
|
|
3318
|
+
|
|
3319
|
+
### 6. Create stylesConfig
|
|
3320
|
+
Map each configuration-id to style PropValues from \`${STYLE_SCHEMA_URI}\`:
|
|
3321
|
+
- Use global variables for colors, sizes, and fonts
|
|
3322
|
+
- Example using global variable:
|
|
3367
3323
|
\`\`\`json
|
|
3368
|
-
{
|
|
3324
|
+
{
|
|
3325
|
+
"heading-1": {
|
|
3326
|
+
"color": { "$$type": "global-color-variable", "value": "primary-color-id" },
|
|
3327
|
+
"font-size": { "$$type": "size", "value": "2rem" }
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3369
3330
|
\`\`\`
|
|
3370
3331
|
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3332
|
+
### 7. Execute build_composition
|
|
3333
|
+
Call the tool with your XML structure, elementConfig, and stylesConfig. The response will contain the created element IDs.
|
|
3334
|
+
At the response you will also find llm_instructions for you to do afterwards, read and follow them!
|
|
3374
3335
|
|
|
3375
|
-
|
|
3376
|
-
for PropValue with array as value:
|
|
3377
|
-
All array types that can receive union types, are typed as mixed array.
|
|
3378
|
-
</note>
|
|
3336
|
+
## Key Points
|
|
3379
3337
|
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3338
|
+
- **PropValue Types**: Arrays that accept union types are typed as mixed arrays
|
|
3339
|
+
- **Visual Sizing**: Widget sizes MUST be defined in stylesConfig. Widget properties like image "size" control resolution, not visual appearance
|
|
3340
|
+
- **Global Variables**: Reference by ID in PropValues (e.g., \`{ "$$type": "global-color-variable", "value": "variable-id" }\`)
|
|
3341
|
+
- **Naming Conventions**: Use meaningful, purpose-based names (e.g., "primary-button", "heading-large"), not value-based names (e.g., "blue-style", "20px-padding")
|
|
3383
3342
|
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
# Advanced operations
|
|
3401
|
-
You are encouraged to run multiple times multiple tools to achieve the desired result.
|
|
3402
|
-
|
|
3403
|
-
An Example scenario of creating fully styled composition:
|
|
3404
|
-
1. Get the list of availble widgets using dynamic resource [${WIDGET_SCHEMA_URI}]
|
|
3405
|
-
2. For each element to create, retreive its configuration schema from [${WIDGET_SCHEMA_URI}/element-name]
|
|
3406
|
-
3. Get the list of available global classes using the always up-to-date resource
|
|
3407
|
-
4. Get the list of available global variables using the dynamic resource
|
|
3408
|
-
5. Build a composition using the "build-composition" tool, providing the structure, configuration and styles for each element. You may want to style the elements later.
|
|
3409
|
-
6. Check you work: as you have the created IDs from the build-composition response, you can retreive each element configuration using the "get-element-configuration-values" tool.
|
|
3410
|
-
7. If needed, update styles using the "configure-element" tool, providing only the styles or widget's properties to update.
|
|
3343
|
+
## Example: e-image PropValue Structure
|
|
3344
|
+
\`\`\`json
|
|
3345
|
+
{
|
|
3346
|
+
"$$type": "image",
|
|
3347
|
+
"value": {
|
|
3348
|
+
"src": {
|
|
3349
|
+
"$$type": "image-src",
|
|
3350
|
+
"value": {
|
|
3351
|
+
"url": { "$$type": "url", "value": "https://example.com/image.jpg" }
|
|
3352
|
+
}
|
|
3353
|
+
},
|
|
3354
|
+
"size": { "$$type": "string", "value": "full" }
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
\`\`\`
|
|
3358
|
+
Note: The "size" property controls image resolution/loading, not visual size. Set visual dimensions in stylesConfig.
|
|
3411
3359
|
`;
|
|
3412
3360
|
|
|
3413
3361
|
// src/prevent-link-in-link-commands.ts
|
|
3414
|
-
var
|
|
3362
|
+
var import_editor_elements10 = require("@elementor/editor-elements");
|
|
3415
3363
|
var import_editor_notifications = require("@elementor/editor-notifications");
|
|
3416
3364
|
var import_editor_v1_adapters11 = require("@elementor/editor-v1-adapters");
|
|
3417
3365
|
var import_i18n2 = require("@wordpress/i18n");
|
|
@@ -3482,13 +3430,13 @@ function shouldBlock(sourceElements, targetElements) {
|
|
|
3482
3430
|
return false;
|
|
3483
3431
|
}
|
|
3484
3432
|
const isSourceContainsAnAnchor = sourceElements.some((src) => {
|
|
3485
|
-
return src?.id ? (0,
|
|
3433
|
+
return src?.id ? (0, import_editor_elements10.isElementAnchored)(src.id) || !!(0, import_editor_elements10.getAnchoredDescendantId)(src.id) : false;
|
|
3486
3434
|
});
|
|
3487
3435
|
if (!isSourceContainsAnAnchor) {
|
|
3488
3436
|
return false;
|
|
3489
3437
|
}
|
|
3490
3438
|
const isTargetContainsAnAnchor = targetElements.some((target) => {
|
|
3491
|
-
return target?.id ? (0,
|
|
3439
|
+
return target?.id ? (0, import_editor_elements10.isElementAnchored)(target.id) || !!(0, import_editor_elements10.getAnchoredAncestorId)(target.id) : false;
|
|
3492
3440
|
});
|
|
3493
3441
|
return isTargetContainsAnAnchor;
|
|
3494
3442
|
}
|
|
@@ -3497,13 +3445,13 @@ function shouldBlock(sourceElements, targetElements) {
|
|
|
3497
3445
|
var import_editor_v1_adapters13 = require("@elementor/editor-v1-adapters");
|
|
3498
3446
|
|
|
3499
3447
|
// src/style-commands/undoable-actions/paste-element-style.ts
|
|
3500
|
-
var
|
|
3448
|
+
var import_editor_elements12 = require("@elementor/editor-elements");
|
|
3501
3449
|
var import_editor_styles_repository4 = require("@elementor/editor-styles-repository");
|
|
3502
3450
|
var import_editor_v1_adapters12 = require("@elementor/editor-v1-adapters");
|
|
3503
3451
|
var import_i18n4 = require("@wordpress/i18n");
|
|
3504
3452
|
|
|
3505
3453
|
// src/style-commands/utils.ts
|
|
3506
|
-
var
|
|
3454
|
+
var import_editor_elements11 = require("@elementor/editor-elements");
|
|
3507
3455
|
var import_editor_props8 = require("@elementor/editor-props");
|
|
3508
3456
|
var import_i18n3 = require("@wordpress/i18n");
|
|
3509
3457
|
function hasAtomicWidgets(args) {
|
|
@@ -3528,7 +3476,7 @@ function getClassesProp(container) {
|
|
|
3528
3476
|
}
|
|
3529
3477
|
function getContainerSchema(container) {
|
|
3530
3478
|
const type = container?.model.get("widgetType") || container?.model.get("elType");
|
|
3531
|
-
const widgetsCache = (0,
|
|
3479
|
+
const widgetsCache = (0, import_editor_elements11.getWidgetsCache)();
|
|
3532
3480
|
const elementType = widgetsCache?.[type];
|
|
3533
3481
|
return elementType?.atomic_props_schema ?? null;
|
|
3534
3482
|
}
|
|
@@ -3541,7 +3489,7 @@ function getClipboardElements(storageKey = "clipboard") {
|
|
|
3541
3489
|
}
|
|
3542
3490
|
}
|
|
3543
3491
|
function getTitleForContainers(containers) {
|
|
3544
|
-
return containers.length > 1 ? (0, import_i18n3.__)("Elements", "elementor") : (0,
|
|
3492
|
+
return containers.length > 1 ? (0, import_i18n3.__)("Elements", "elementor") : (0, import_editor_elements11.getElementLabel)(containers[0].id);
|
|
3545
3493
|
}
|
|
3546
3494
|
|
|
3547
3495
|
// src/style-commands/undoable-actions/paste-element-style.ts
|
|
@@ -3554,7 +3502,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters12.undoable)(
|
|
|
3554
3502
|
if (!classesProp) {
|
|
3555
3503
|
return null;
|
|
3556
3504
|
}
|
|
3557
|
-
const originalStyles = (0,
|
|
3505
|
+
const originalStyles = (0, import_editor_elements12.getElementStyles)(container.id);
|
|
3558
3506
|
const [styleId, styleDef] = Object.entries(originalStyles ?? {})[0] ?? [];
|
|
3559
3507
|
const originalStyle = Object.keys(styleDef ?? {}).length ? styleDef : null;
|
|
3560
3508
|
const revertData = {
|
|
@@ -3563,7 +3511,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters12.undoable)(
|
|
|
3563
3511
|
};
|
|
3564
3512
|
if (styleId) {
|
|
3565
3513
|
newStyle.variants.forEach(({ meta, props, custom_css: customCss }) => {
|
|
3566
|
-
(0,
|
|
3514
|
+
(0, import_editor_elements12.updateElementStyle)({
|
|
3567
3515
|
elementId,
|
|
3568
3516
|
styleId,
|
|
3569
3517
|
meta,
|
|
@@ -3574,7 +3522,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters12.undoable)(
|
|
|
3574
3522
|
} else {
|
|
3575
3523
|
const [firstVariant] = newStyle.variants;
|
|
3576
3524
|
const additionalVariants = newStyle.variants.slice(1);
|
|
3577
|
-
revertData.styleId = (0,
|
|
3525
|
+
revertData.styleId = (0, import_editor_elements12.createElementStyle)({
|
|
3578
3526
|
elementId,
|
|
3579
3527
|
classesProp,
|
|
3580
3528
|
label: import_editor_styles_repository4.ELEMENTS_STYLES_RESERVED_LABEL,
|
|
@@ -3592,7 +3540,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters12.undoable)(
|
|
|
3592
3540
|
return;
|
|
3593
3541
|
}
|
|
3594
3542
|
if (!revertData.originalStyle) {
|
|
3595
|
-
(0,
|
|
3543
|
+
(0, import_editor_elements12.deleteElementStyle)(container.id, revertData.styleId);
|
|
3596
3544
|
return;
|
|
3597
3545
|
}
|
|
3598
3546
|
const classesProp = getClassesProp(container);
|
|
@@ -3601,7 +3549,7 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters12.undoable)(
|
|
|
3601
3549
|
}
|
|
3602
3550
|
const [firstVariant] = revertData.originalStyle.variants;
|
|
3603
3551
|
const additionalVariants = revertData.originalStyle.variants.slice(1);
|
|
3604
|
-
(0,
|
|
3552
|
+
(0, import_editor_elements12.createElementStyle)({
|
|
3605
3553
|
elementId: container.id,
|
|
3606
3554
|
classesProp,
|
|
3607
3555
|
label: import_editor_styles_repository4.ELEMENTS_STYLES_RESERVED_LABEL,
|
|
@@ -3653,7 +3601,7 @@ function pasteStyles(args, pasteCallback) {
|
|
|
3653
3601
|
var import_editor_v1_adapters15 = require("@elementor/editor-v1-adapters");
|
|
3654
3602
|
|
|
3655
3603
|
// src/style-commands/undoable-actions/reset-element-style.ts
|
|
3656
|
-
var
|
|
3604
|
+
var import_editor_elements13 = require("@elementor/editor-elements");
|
|
3657
3605
|
var import_editor_styles_repository5 = require("@elementor/editor-styles-repository");
|
|
3658
3606
|
var import_editor_v1_adapters14 = require("@elementor/editor-v1-adapters");
|
|
3659
3607
|
var import_i18n5 = require("@wordpress/i18n");
|
|
@@ -3662,9 +3610,9 @@ var undoableResetElementStyle = () => (0, import_editor_v1_adapters14.undoable)(
|
|
|
3662
3610
|
do: ({ containers }) => {
|
|
3663
3611
|
return containers.map((container) => {
|
|
3664
3612
|
const elementId = container.model.get("id");
|
|
3665
|
-
const containerStyles = (0,
|
|
3613
|
+
const containerStyles = (0, import_editor_elements13.getElementStyles)(elementId);
|
|
3666
3614
|
Object.keys(containerStyles ?? {}).forEach(
|
|
3667
|
-
(styleId) => (0,
|
|
3615
|
+
(styleId) => (0, import_editor_elements13.deleteElementStyle)(elementId, styleId)
|
|
3668
3616
|
);
|
|
3669
3617
|
return containerStyles;
|
|
3670
3618
|
});
|
|
@@ -3680,7 +3628,7 @@ var undoableResetElementStyle = () => (0, import_editor_v1_adapters14.undoable)(
|
|
|
3680
3628
|
Object.entries(containerStyles ?? {}).forEach(([styleId, style]) => {
|
|
3681
3629
|
const [firstVariant] = style.variants;
|
|
3682
3630
|
const additionalVariants = style.variants.slice(1);
|
|
3683
|
-
(0,
|
|
3631
|
+
(0, import_editor_elements13.createElementStyle)({
|
|
3684
3632
|
elementId,
|
|
3685
3633
|
classesProp,
|
|
3686
3634
|
styleId,
|