@elementor/editor-canvas 4.2.0-938 → 4.2.0-940

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.mjs CHANGED
@@ -1396,7 +1396,7 @@ function createPropsResolver({ transformers, schema: initialSchema, onPropResolv
1396
1396
  Object.entries(schema2).map(async ([key, type]) => {
1397
1397
  const value = props[key] ?? type.default;
1398
1398
  const transformed = await transform({ value, key, type, signal, renderContext });
1399
- onPropResolve?.({ key, value: transformed });
1399
+ onPropResolve?.({ key, value: transformed, propValue: value, propType: type });
1400
1400
  if (isMultiProps(transformed)) {
1401
1401
  return getMultiPropsValue(transformed);
1402
1402
  }
@@ -1466,6 +1466,27 @@ function createPropsResolver({ transformers, schema: initialSchema, onPropResolv
1466
1466
  return resolve;
1467
1467
  }
1468
1468
 
1469
+ // src/renderers/enqueue-font-from-style-prop.ts
1470
+ import { getPropSchemaFromCache, isTransformable as isTransformable2 } from "@elementor/editor-props";
1471
+ var maybeEnqueueFontFromStyleProp = (propType, propValue, enqueue) => {
1472
+ if (!isTransformable2(propValue) || propValue.disabled) {
1473
+ return;
1474
+ }
1475
+ const typeKey = propType.kind === "union" ? propValue.$$type : propType.key;
1476
+ const propTypeUtil = getPropSchemaFromCache(typeKey);
1477
+ if (!propTypeUtil || !("getEnqueueFontFamily" in propTypeUtil) || typeof propTypeUtil.getEnqueueFontFamily !== "function") {
1478
+ return;
1479
+ }
1480
+ const stored = propValue.value;
1481
+ if (typeof stored !== "string") {
1482
+ return;
1483
+ }
1484
+ const font = propTypeUtil.getEnqueueFontFamily(stored);
1485
+ if (font) {
1486
+ enqueue(font);
1487
+ }
1488
+ };
1489
+
1469
1490
  // src/transformers/create-transformers-registry.ts
1470
1491
  function createTransformersRegistry() {
1471
1492
  const transformers = {};
@@ -1498,11 +1519,8 @@ function useStylePropResolver() {
1498
1519
  return createPropsResolver({
1499
1520
  transformers: styleTransformersRegistry,
1500
1521
  schema: getStylesSchema(),
1501
- onPropResolve: ({ key, value }) => {
1502
- if (key !== "font-family" || typeof value !== "string") {
1503
- return;
1504
- }
1505
- enqueueFont(value);
1522
+ onPropResolve: ({ propValue, propType }) => {
1523
+ maybeEnqueueFontFromStyleProp(propType, propValue, enqueueFont);
1506
1524
  }
1507
1525
  });
1508
1526
  }, []);
@@ -2432,6 +2450,18 @@ var flexTransformer = createTransformer((value) => {
2432
2450
  return null;
2433
2451
  });
2434
2452
 
2453
+ // src/transformers/styles/font-family-transformer.ts
2454
+ var fontFamilyTransformer = createTransformer((value) => {
2455
+ if (typeof value !== "string" || !value.trim()) {
2456
+ return null;
2457
+ }
2458
+ const trimmed = value.trim();
2459
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
2460
+ return trimmed;
2461
+ }
2462
+ return `"${trimmed}"`;
2463
+ });
2464
+
2435
2465
  // src/transformers/styles/grid-track-renderer.ts
2436
2466
  var GRID_TRACK_PROPERTIES = /* @__PURE__ */ new Set(["grid-template-columns", "grid-template-rows"]);
2437
2467
  var isGridTrackProperty = (cssProperty) => GRID_TRACK_PROPERTIES.has(cssProperty);
@@ -2583,7 +2613,7 @@ var mapToTransitionString = (value, allowedProperties) => {
2583
2613
 
2584
2614
  // src/init-style-transformers.ts
2585
2615
  function initStyleTransformers() {
2586
- styleTransformersRegistry.register("size", sizeTransformer).register("grid-track-size", gridTrackSizeTransformer).register("shadow", shadowTransformer).register("stroke", strokeTransformer).register(
2616
+ styleTransformersRegistry.register("font-family", fontFamilyTransformer).register("size", sizeTransformer).register("grid-track-size", gridTrackSizeTransformer).register("shadow", shadowTransformer).register("stroke", strokeTransformer).register(
2587
2617
  "dimensions",
2588
2618
  createMultiPropsTransformer(
2589
2619
  ["block-start", "block-end", "inline-start", "inline-end"],
@@ -4164,7 +4194,7 @@ function extractElementData(element) {
4164
4194
  widgetType: model.widgetType || void 0,
4165
4195
  version: resolveElementVersion(element)
4166
4196
  };
4167
- const title = model.title || element.model?.editor_settings?.title;
4197
+ const title = model.title || element.model?.editor_settings?.title || element.model.getTitle?.();
4168
4198
  if (title) {
4169
4199
  result.title = title;
4170
4200
  }
@@ -4599,7 +4629,7 @@ import {
4599
4629
  createElement as createElement13,
4600
4630
  deleteElement as deleteElement2,
4601
4631
  getContainer as getContainer5,
4602
- getWidgetsCache as getWidgetsCache8
4632
+ getWidgetsCache as getWidgetsCache9
4603
4633
  } from "@elementor/editor-elements";
4604
4634
 
4605
4635
  // src/composition-builder/composition-builder.ts
@@ -4608,18 +4638,18 @@ import {
4608
4638
  deleteElement,
4609
4639
  generateElementId as generateElementId2,
4610
4640
  getContainer as getContainer4,
4611
- getWidgetsCache as getWidgetsCache7
4641
+ getWidgetsCache as getWidgetsCache8
4612
4642
  } from "@elementor/editor-elements";
4613
4643
 
4614
4644
  // src/mcp/utils/do-update-element-property.ts
4615
4645
  import {
4616
4646
  createElementStyle,
4617
4647
  getElementStyles,
4618
- getWidgetsCache as getWidgetsCache6,
4648
+ getWidgetsCache as getWidgetsCache7,
4619
4649
  updateElementSettings,
4620
4650
  updateElementStyle
4621
4651
  } from "@elementor/editor-elements";
4622
- import { getPropSchemaFromCache, Schema as Schema3 } from "@elementor/editor-props";
4652
+ import { getPropSchemaFromCache as getPropSchemaFromCache2, Schema as Schema3 } from "@elementor/editor-props";
4623
4653
  import { getStylesSchema as getStylesSchema2, getVariantByMeta } from "@elementor/editor-styles";
4624
4654
  import { __privateRunCommandSync as runCommandSync2 } from "@elementor/editor-v1-adapters";
4625
4655
 
@@ -4637,6 +4667,58 @@ var readStoredCustomCssText = (raw) => {
4637
4667
  }
4638
4668
  };
4639
4669
 
4670
+ // src/mcp/utils/resolve-canonical-prop-name.ts
4671
+ import { getWidgetsCache as getWidgetsCache6 } from "@elementor/editor-elements";
4672
+ function buildAliasToCanonicalMap(schema2) {
4673
+ const aliasToCanonical = {};
4674
+ for (const [canonical, propType] of Object.entries(schema2)) {
4675
+ const aliases = propType.meta?.aliases;
4676
+ if (!Array.isArray(aliases)) {
4677
+ continue;
4678
+ }
4679
+ for (const alias of aliases) {
4680
+ if (typeof alias === "string" && alias) {
4681
+ aliasToCanonical[alias] = canonical;
4682
+ }
4683
+ }
4684
+ }
4685
+ return aliasToCanonical;
4686
+ }
4687
+ function resolveCanonicalPropName(elementType, propertyName) {
4688
+ const schema2 = getWidgetsCache6()?.[elementType]?.atomic_props_schema;
4689
+ if (!schema2 || schema2[propertyName]) {
4690
+ return propertyName;
4691
+ }
4692
+ return buildAliasToCanonicalMap(schema2)[propertyName] ?? propertyName;
4693
+ }
4694
+ function resolveCanonicalPropKeys(elementType, props) {
4695
+ const schema2 = getWidgetsCache6()?.[elementType]?.atomic_props_schema;
4696
+ if (!schema2) {
4697
+ return { ...props };
4698
+ }
4699
+ const aliasToCanonical = buildAliasToCanonicalMap(schema2);
4700
+ const resolved = {};
4701
+ for (const [key, value] of Object.entries(props)) {
4702
+ if (schema2[key]) {
4703
+ resolved[key] = value;
4704
+ }
4705
+ }
4706
+ for (const [key, value] of Object.entries(props)) {
4707
+ if (schema2[key]) {
4708
+ continue;
4709
+ }
4710
+ const canonical = aliasToCanonical[key];
4711
+ if (!canonical) {
4712
+ resolved[key] = value;
4713
+ continue;
4714
+ }
4715
+ if (!Object.prototype.hasOwnProperty.call(resolved, canonical)) {
4716
+ resolved[canonical] = value;
4717
+ }
4718
+ }
4719
+ return resolved;
4720
+ }
4721
+
4640
4722
  // src/mcp/utils/do-update-element-property.ts
4641
4723
  var LOCAL_STYLE_META = {
4642
4724
  breakpoint: "desktop",
@@ -4653,7 +4735,8 @@ function resolvePropValue(value, forceKey) {
4653
4735
  });
4654
4736
  }
4655
4737
  var doUpdateElementProperty = (params) => {
4656
- const { elementId, propertyName, propertyValue, elementType, customCssWriteMode = "replace" } = params;
4738
+ const { elementId, propertyValue, elementType, customCssWriteMode = "replace" } = params;
4739
+ const propertyName = params.propertyName === "_styles" ? params.propertyName : resolveCanonicalPropName(elementType, params.propertyName);
4657
4740
  if (propertyName === "_styles") {
4658
4741
  const elementStyles = getElementStyles(elementId) || {};
4659
4742
  const propertyMapValue = propertyValue;
@@ -4702,7 +4785,7 @@ var doUpdateElementProperty = (params) => {
4702
4785
  }
4703
4786
  if (propertyRawSchema.kind === "plain") {
4704
4787
  if (typeof propertyMapValue[stylePropName] !== "object") {
4705
- const propUtil = getPropSchemaFromCache(propertyRawSchema.key);
4788
+ const propUtil = getPropSchemaFromCache2(propertyRawSchema.key);
4706
4789
  if (propUtil) {
4707
4790
  const plainValue = propUtil.create(propertyMapValue[stylePropName]);
4708
4791
  propertyMapValue[stylePropName] = plainValue;
@@ -4741,7 +4824,7 @@ var doUpdateElementProperty = (params) => {
4741
4824
  }
4742
4825
  return;
4743
4826
  }
4744
- const elementPropSchema = getWidgetsCache6()?.[elementType]?.atomic_props_schema;
4827
+ const elementPropSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
4745
4828
  if (!elementPropSchema) {
4746
4829
  throw new Error(`No prop schema found for element type: ${elementType}`);
4747
4830
  }
@@ -4824,7 +4907,7 @@ var CompositionBuilder = class _CompositionBuilder {
4824
4907
  api = {
4825
4908
  createElement: createElement12,
4826
4909
  deleteElement,
4827
- getWidgetsCache: getWidgetsCache7,
4910
+ getWidgetsCache: getWidgetsCache8,
4828
4911
  generateElementId: generateElementId2,
4829
4912
  getContainer: getContainer4,
4830
4913
  doUpdateElementProperty
@@ -5346,7 +5429,7 @@ var initBuildCompositionsTool = (reg) => {
5346
5429
  const { xmlStructure, elementConfig, stylesConfig } = adaptLeafRootParams({
5347
5430
  ...rawParams,
5348
5431
  stylesConfig: convertedStyles,
5349
- widgetsCache: getWidgetsCache8() ?? {}
5432
+ widgetsCache: getWidgetsCache9() ?? {}
5350
5433
  });
5351
5434
  let generatedXML = "";
5352
5435
  const errors = [];
@@ -5358,7 +5441,7 @@ var initBuildCompositionsTool = (reg) => {
5358
5441
  const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
5359
5442
  createElement: createElement13,
5360
5443
  deleteElement: deleteElement2,
5361
- getWidgetsCache: getWidgetsCache8
5444
+ getWidgetsCache: getWidgetsCache9
5362
5445
  });
5363
5446
  compositionBuilder.setElementConfig(elementConfig);
5364
5447
  compositionBuilder.setStylesConfig(stylesConfig);
@@ -5443,7 +5526,7 @@ function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
5443
5526
  if (doc.querySelector("parsererror")) {
5444
5527
  throw new Error("Failed to parse XML string: " + doc);
5445
5528
  }
5446
- const widgetsCache = getWidgetsCache8() ?? {};
5529
+ const widgetsCache = getWidgetsCache9() ?? {};
5447
5530
  for (const node of doc.querySelectorAll("*")) {
5448
5531
  const type = node.tagName;
5449
5532
  const widgetData = widgetsCache[type];
@@ -5460,10 +5543,10 @@ function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
5460
5543
  }
5461
5544
 
5462
5545
  // src/mcp/tools/configure-element/tool.ts
5463
- import { getWidgetsCache as getWidgetsCache10 } from "@elementor/editor-elements";
5546
+ import { getContainer as getContainer6, getWidgetsCache as getWidgetsCache11 } from "@elementor/editor-elements";
5464
5547
 
5465
5548
  // src/mcp/utils/validate-input.ts
5466
- import { getWidgetsCache as getWidgetsCache9 } from "@elementor/editor-elements";
5549
+ import { getWidgetsCache as getWidgetsCache10 } from "@elementor/editor-elements";
5467
5550
  import { Schema as Schema4 } from "@elementor/editor-props";
5468
5551
  import { getStylesSchema as getStylesSchema3 } from "@elementor/editor-styles";
5469
5552
  var _widgetsSchema = null;
@@ -5471,7 +5554,7 @@ var validateInput = {
5471
5554
  get widgetsSchema() {
5472
5555
  if (!_widgetsSchema) {
5473
5556
  const schema2 = {};
5474
- const cache = getWidgetsCache9();
5557
+ const cache = getWidgetsCache10();
5475
5558
  if (!cache) {
5476
5559
  return {};
5477
5560
  }
@@ -5712,19 +5795,28 @@ var initConfigureElementTool = (reg) => {
5712
5795
  { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5713
5796
  ],
5714
5797
  handler: async ({ elementId, propertiesToChange, elementType, style }) => {
5715
- const widgetData = getWidgetsCache10()?.[elementType];
5798
+ const widgetData = getWidgetsCache11()?.[elementType];
5716
5799
  if (!widgetData) {
5717
5800
  throw new Error(
5718
5801
  `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
5719
5802
  );
5720
5803
  }
5804
+ const container = getContainer6(elementId);
5805
+ if (!container) {
5806
+ throw new Error(`Element with id ${elementId} not found`);
5807
+ }
5808
+ const isElementTypeMatchingId = container.settings.get("widgetType") === elementType || container.type === elementType;
5809
+ if (!isElementTypeMatchingId) {
5810
+ throw new Error(`Element with ID ${elementId} is not of type ${elementType}`);
5811
+ }
5721
5812
  if (!widgetData.atomic_props_schema) {
5722
5813
  throw new Error(
5723
5814
  `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${elementType}`
5724
5815
  );
5725
5816
  }
5726
- const toUpdate = Object.entries(propertiesToChange);
5727
- const { valid, errors } = validateInput.validatePropSchema(elementType, propertiesToChange);
5817
+ const propertiesToUpdate = resolveCanonicalPropKeys(elementType, propertiesToChange);
5818
+ const toUpdate = Object.entries(propertiesToUpdate);
5819
+ const { valid, errors } = validateInput.validatePropSchema(elementType, propertiesToUpdate);
5728
5820
  if (!valid) {
5729
5821
  const errorMessage = `Failed to configure element "${elementId}" due to invalid properties: ${errors?.join(
5730
5822
  "\n- "
@@ -5804,7 +5896,7 @@ Provide styling as raw CSS via the "style" parameter (a flat map of CSS property
5804
5896
  }
5805
5897
 
5806
5898
  // src/mcp/tools/get-element-config/tool.ts
5807
- import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache11 } from "@elementor/editor-elements";
5899
+ import { getContainer as getContainer7, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache12 } from "@elementor/editor-elements";
5808
5900
  import { Schema as Schema5 } from "@elementor/editor-props";
5809
5901
  import { z as z3 } from "@elementor/schema";
5810
5902
  var schema = {
@@ -5839,12 +5931,12 @@ var initGetElementConfigTool = (reg) => {
5839
5931
  schema,
5840
5932
  outputSchema: outputSchema3,
5841
5933
  handler: async ({ elementId }) => {
5842
- const element = getContainer6(elementId);
5934
+ const element = getContainer7(elementId);
5843
5935
  if (!element) {
5844
5936
  throw new Error(`Element with ID ${elementId} not found.`);
5845
5937
  }
5846
5938
  const elementType = element.model.get("widgetType") || element.model.get("elType") || "";
5847
- const widgetData = getWidgetsCache11()?.[elementType];
5939
+ const widgetData = getWidgetsCache12()?.[elementType];
5848
5940
  if (!widgetData) {
5849
5941
  throw new Error(
5850
5942
  `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
@@ -5856,7 +5948,7 @@ var initGetElementConfigTool = (reg) => {
5856
5948
  );
5857
5949
  }
5858
5950
  const elementRawSettings = element.settings;
5859
- const propSchema = getWidgetsCache11()?.[elementType]?.atomic_props_schema;
5951
+ const propSchema = getWidgetsCache12()?.[elementType]?.atomic_props_schema;
5860
5952
  if (!elementRawSettings || !propSchema) {
5861
5953
  throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
5862
5954
  }
@@ -6108,7 +6200,7 @@ function shouldBlock(sourceElements, targetElements) {
6108
6200
  }
6109
6201
 
6110
6202
  // src/style-commands/paste-style.ts
6111
- import { getContainer as getContainer7, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
6203
+ import { getContainer as getContainer8, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
6112
6204
  import { classesPropTypeUtil } from "@elementor/editor-props";
6113
6205
  import {
6114
6206
  __privateListenTo as listenTo5,
@@ -6117,7 +6209,7 @@ import {
6117
6209
  } from "@elementor/editor-v1-adapters";
6118
6210
 
6119
6211
  // src/utils/command-utils.ts
6120
- import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache12 } from "@elementor/editor-elements";
6212
+ import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache13 } from "@elementor/editor-elements";
6121
6213
  import { CLASSES_PROP_KEY } from "@elementor/editor-props";
6122
6214
  import { __ as __5 } from "@wordpress/i18n";
6123
6215
  function hasAtomicWidgets(args) {
@@ -6142,7 +6234,7 @@ function getClassesProp(container) {
6142
6234
  }
6143
6235
  function getContainerSchema(container) {
6144
6236
  const type = container?.model.get("widgetType") || container?.model.get("elType");
6145
- const widgetsCache = getWidgetsCache12();
6237
+ const widgetsCache = getWidgetsCache13();
6146
6238
  const elementType = widgetsCache?.[type];
6147
6239
  return elementType?.atomic_props_schema ?? null;
6148
6240
  }
@@ -6261,7 +6353,7 @@ function pasteStyles(args, pasteLocalStyle) {
6261
6353
  }
6262
6354
  const clipboardElements = getClipboardElements(storageKey);
6263
6355
  const [clipboardElement] = clipboardElements ?? [];
6264
- const clipboardContainer = getContainer7(clipboardElement.id);
6356
+ const clipboardContainer = getContainer8(clipboardElement.id);
6265
6357
  if (!clipboardElement || !clipboardContainer || !isAtomicWidget(clipboardContainer)) {
6266
6358
  return;
6267
6359
  }
@@ -6538,10 +6630,10 @@ function useEscapeOnCanvas(canvasDocument, onEscape) {
6538
6630
  }
6539
6631
 
6540
6632
  // src/utils/after-render.ts
6541
- import { getContainer as getContainer8 } from "@elementor/editor-elements";
6633
+ import { getContainer as getContainer9 } from "@elementor/editor-elements";
6542
6634
  function doAfterRender(elementIds, callback) {
6543
6635
  const pending = elementIds.map((elementId) => {
6544
- const view = getContainer8(elementId)?.view;
6636
+ const view = getContainer9(elementId)?.view;
6545
6637
  if (!view || !hasDoAfterRender(view)) {
6546
6638
  return void 0;
6547
6639
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@elementor/editor-canvas",
3
3
  "description": "Elementor Editor Canvas",
4
- "version": "4.2.0-938",
4
+ "version": "4.2.0-940",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,26 +37,26 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "4.2.0-938",
40
+ "@elementor/editor": "4.2.0-940",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.2.0-938",
43
- "@elementor/editor-documents": "4.2.0-938",
44
- "@elementor/editor-elements": "4.2.0-938",
45
- "@elementor/editor-interactions": "4.2.0-938",
46
- "@elementor/editor-mcp": "4.2.0-938",
47
- "@elementor/editor-notifications": "4.2.0-938",
48
- "@elementor/editor-props": "4.2.0-938",
49
- "@elementor/editor-responsive": "4.2.0-938",
50
- "@elementor/editor-styles": "4.2.0-938",
51
- "@elementor/editor-styles-repository": "4.2.0-938",
52
- "@elementor/editor-ui": "4.2.0-938",
53
- "@elementor/editor-v1-adapters": "4.2.0-938",
54
- "@elementor/http-client": "4.2.0-938",
55
- "@elementor/schema": "4.2.0-938",
56
- "@elementor/twing": "4.2.0-938",
42
+ "@elementor/editor-controls": "4.2.0-940",
43
+ "@elementor/editor-documents": "4.2.0-940",
44
+ "@elementor/editor-elements": "4.2.0-940",
45
+ "@elementor/editor-interactions": "4.2.0-940",
46
+ "@elementor/editor-mcp": "4.2.0-940",
47
+ "@elementor/editor-notifications": "4.2.0-940",
48
+ "@elementor/editor-props": "4.2.0-940",
49
+ "@elementor/editor-responsive": "4.2.0-940",
50
+ "@elementor/editor-styles": "4.2.0-940",
51
+ "@elementor/editor-styles-repository": "4.2.0-940",
52
+ "@elementor/editor-ui": "4.2.0-940",
53
+ "@elementor/editor-v1-adapters": "4.2.0-940",
54
+ "@elementor/http-client": "4.2.0-940",
55
+ "@elementor/schema": "4.2.0-940",
56
+ "@elementor/twing": "4.2.0-940",
57
57
  "@elementor/ui": "1.37.5",
58
- "@elementor/utils": "4.2.0-938",
59
- "@elementor/wp-media": "4.2.0-938",
58
+ "@elementor/utils": "4.2.0-940",
59
+ "@elementor/wp-media": "4.2.0-940",
60
60
  "@floating-ui/react": "^0.27.5",
61
61
  "@wordpress/i18n": "^5.13.0"
62
62
  },
@@ -1,51 +1,60 @@
1
1
  import { createMockPropType } from 'test-utils';
2
- import { stringPropTypeUtil } from '@elementor/editor-props';
2
+ import { fontFamilyPropTypeUtil } from '@elementor/editor-props';
3
+ import { type BreakpointsMap } from '@elementor/editor-responsive';
3
4
  import { getStylesSchema } from '@elementor/editor-styles';
4
5
  import { enqueueFont } from '@elementor/editor-v1-adapters';
5
- import { act, renderHook } from '@testing-library/react';
6
+ import { renderHook } from '@testing-library/react';
6
7
 
7
8
  import { initStyleTransformers } from '../../init-style-transformers';
9
+ import { createStylesRenderer } from '../../renderers/create-styles-renderer';
8
10
  import { useStylePropResolver } from '../use-style-prop-resolver';
9
11
 
10
12
  jest.mock( '@elementor/editor-v1-adapters' );
11
- jest.mock( '@elementor/editor-styles' );
13
+ jest.mock( '@elementor/editor-styles', () => ( {
14
+ ...jest.requireActual( '@elementor/editor-styles' ),
15
+ getStylesSchema: jest.fn(),
16
+ } ) );
12
17
 
13
18
  describe( 'useStylePropResolver', () => {
14
- it( 'should call enqueueFont with the correct value when font-family is resolved', async () => {
19
+ it( 'should enqueue fonts from stored props when rendering styles', async () => {
15
20
  // Arrange.
16
21
  initStyleTransformers();
17
22
 
18
23
  jest.mocked( getStylesSchema ).mockReturnValue( {
19
- 'font-family': createMockPropType( { key: 'string', kind: 'plain' } ),
20
- 'another-prop': createMockPropType( { key: 'string', kind: 'plain' } ),
24
+ 'font-family': createMockPropType( { key: 'font-family', kind: 'plain' } ),
25
+ color: createMockPropType( { key: 'string', kind: 'plain' } ),
21
26
  } );
22
27
 
23
28
  const { result } = renderHook( useStylePropResolver );
24
29
 
25
- // Act.
26
- const resolve = result.current;
27
-
28
- await act( () =>
29
- resolve( {
30
- props: {
31
- 'another-prop': stringPropTypeUtil.create( 'value' ),
32
- },
33
- } )
34
- );
35
-
36
- // Assert.
37
- expect( enqueueFont ).not.toHaveBeenCalled();
30
+ const view = createStylesRenderer( {
31
+ resolve: result.current,
32
+ breakpoints: {} as BreakpointsMap,
33
+ } );
38
34
 
39
35
  // Act.
40
- await act( () =>
41
- resolve( {
42
- props: {
43
- 'font-family': stringPropTypeUtil.create( 'arial' ),
36
+ await view( {
37
+ styles: [
38
+ {
39
+ id: 'test-style',
40
+ type: 'class',
41
+ cssName: 'test-style',
42
+ label: 'Test style',
43
+ variants: [
44
+ {
45
+ meta: { breakpoint: null, state: null },
46
+ props: {
47
+ 'font-family': fontFamilyPropTypeUtil.create( 'Open Sans' ),
48
+ },
49
+ custom_css: null,
50
+ },
51
+ ],
44
52
  },
45
- } )
46
- );
53
+ ],
54
+ } );
47
55
 
48
56
  // Assert.
49
- expect( enqueueFont ).toHaveBeenCalledWith( 'arial' );
57
+ expect( enqueueFont ).toHaveBeenCalledWith( 'Open Sans' );
58
+ expect( enqueueFont ).toHaveBeenCalledTimes( 1 );
50
59
  } );
51
60
  } );
@@ -3,6 +3,7 @@ import { getStylesSchema } from '@elementor/editor-styles';
3
3
  import { enqueueFont } from '@elementor/editor-v1-adapters';
4
4
 
5
5
  import { createPropsResolver } from '../renderers/create-props-resolver';
6
+ import { maybeEnqueueFontFromStyleProp } from '../renderers/enqueue-font-from-style-prop';
6
7
  import { styleTransformersRegistry } from '../style-transformers-registry';
7
8
 
8
9
  export function useStylePropResolver() {
@@ -10,12 +11,8 @@ export function useStylePropResolver() {
10
11
  return createPropsResolver( {
11
12
  transformers: styleTransformersRegistry,
12
13
  schema: getStylesSchema(),
13
- onPropResolve: ( { key, value } ) => {
14
- if ( key !== 'font-family' || typeof value !== 'string' ) {
15
- return;
16
- }
17
-
18
- enqueueFont( value );
14
+ onPropResolve: ( { propValue, propType } ) => {
15
+ maybeEnqueueFontFromStyleProp( propType, propValue, enqueueFont );
19
16
  },
20
17
  } );
21
18
  }, [] );
@@ -13,6 +13,7 @@ import { createCombineArrayTransformer } from './transformers/styles/create-comb
13
13
  import { createMultiPropsTransformer } from './transformers/styles/create-multi-props-transformer';
14
14
  import { filterTransformer } from './transformers/styles/filter-transformer';
15
15
  import { flexTransformer } from './transformers/styles/flex-transformer';
16
+ import { fontFamilyTransformer } from './transformers/styles/font-family-transformer';
16
17
  import { gridTrackSizeTransformer } from './transformers/styles/grid-track-size-transformer';
17
18
  import { perspectiveOriginTransformer } from './transformers/styles/perspective-origin-transformer';
18
19
  import { positionTransformer } from './transformers/styles/position-transformer';
@@ -30,6 +31,7 @@ import { transitionTransformer } from './transformers/styles/transition-transfor
30
31
 
31
32
  export function initStyleTransformers() {
32
33
  styleTransformersRegistry
34
+ .register( 'font-family', fontFamilyTransformer )
33
35
  .register( 'size', sizeTransformer )
34
36
  .register( 'grid-track-size', gridTrackSizeTransformer )
35
37
  .register( 'shadow', shadowTransformer )
@@ -136,7 +136,10 @@ function extractElementData( element: UnknownVersionElementInstanceData ): Recor
136
136
  version: resolveElementVersion( element ),
137
137
  };
138
138
 
139
- const title = model.title || element.model?.editor_settings?.title;
139
+ const title =
140
+ model.title ||
141
+ element.model?.editor_settings?.title ||
142
+ ( element.model as unknown as Record< string, CallableFunction > ).getTitle?.();
140
143
 
141
144
  if ( title ) {
142
145
  result.title = title;
@@ -1,10 +1,12 @@
1
- import { getWidgetsCache } from '@elementor/editor-elements';
1
+ import { getContainer, getWidgetsCache } from '@elementor/editor-elements';
2
2
  import { type MCPRegistryEntry } from '@elementor/editor-mcp';
3
+ import { type PropValue } from '@elementor/editor-props';
3
4
 
4
5
  import { DYNAMIC_TAGS_URI } from '../../resources/dynamic-tags-resource';
5
6
  import { WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
6
7
  import { convertCssToAtomic } from '../../utils/convert-css-to-atomic';
7
8
  import { doUpdateElementProperty } from '../../utils/do-update-element-property';
9
+ import { resolveCanonicalPropKeys } from '../../utils/resolve-canonical-prop-name';
8
10
  import { validateInput } from '../../utils/validate-input';
9
11
  import { CONFIGURE_ELEMENT_GUIDE_URI, generatePrompt } from './prompt';
10
12
  import { inputSchema as schema, outputSchema } from './schema';
@@ -42,20 +44,31 @@ export const initConfigureElementTool = ( reg: MCPRegistryEntry ) => {
42
44
  `Unknown element type: ${ elementType }. Check the available-widgets resource for valid types.`
43
45
  );
44
46
  }
47
+ const container = getContainer( elementId );
48
+ if ( ! container ) {
49
+ throw new Error( `Element with id ${ elementId } not found` );
50
+ }
51
+ const isElementTypeMatchingId =
52
+ container.settings.get( 'widgetType' ) === elementType ||
53
+ ( container as Record< string, unknown > ).type === elementType;
54
+ if ( ! isElementTypeMatchingId ) {
55
+ throw new Error( `Element with ID ${ elementId } is not of type ${ elementType }` );
56
+ }
45
57
  if ( ! widgetData.atomic_props_schema ) {
46
58
  throw new Error(
47
59
  `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${ elementType }`
48
60
  );
49
61
  }
50
- const toUpdate = Object.entries( propertiesToChange );
51
- const { valid, errors } = validateInput.validatePropSchema( elementType, propertiesToChange );
62
+ const propertiesToUpdate = resolveCanonicalPropKeys( elementType, propertiesToChange );
63
+ const toUpdate = Object.entries( propertiesToUpdate );
64
+ const { valid, errors } = validateInput.validatePropSchema( elementType, propertiesToUpdate );
52
65
  if ( ! valid ) {
53
66
  const errorMessage = `Failed to configure element "${ elementId }" due to invalid properties: ${ errors?.join(
54
67
  '\n- '
55
68
  ) }`;
56
69
  throw new Error( errorMessage );
57
70
  }
58
- for ( const [ propertyName, propertyValue ] of toUpdate ) {
71
+ for ( const [ propertyName, propertyValue ] of toUpdate as [ string, PropValue ][] ) {
59
72
  try {
60
73
  doUpdateElementProperty( {
61
74
  elementId,
@@ -143,6 +143,49 @@ describe( 'doUpdateElementProperty', () => {
143
143
  );
144
144
  } );
145
145
 
146
+ it( 'persists canonical prop key when an alias property name is used', () => {
147
+ // Arrange
148
+ const propertyValue = 'resolved-title';
149
+ jest.mocked( getWidgetsCache ).mockReturnValue( {
150
+ [ ELEMENT_TYPE ]: {
151
+ atomic_props_schema: {
152
+ // @ts-ignore: Mock type
153
+ [ PROPERTY_NAME ]: {
154
+ ...PROP_SCHEMA_ENTRY,
155
+ meta: { aliases: [ 'text' ] },
156
+ },
157
+ },
158
+ },
159
+ } );
160
+ jest.mocked( Schema.validatePropValue ).mockReturnValue( {
161
+ jsonSchema: EXPECTED_JSON_SCHEMA_SNIPPET,
162
+ valid: true,
163
+ errorMessages: [],
164
+ errors: [],
165
+ } );
166
+
167
+ // Act
168
+ doUpdateElementProperty( {
169
+ elementId: ELEMENT_ID,
170
+ elementType: ELEMENT_TYPE,
171
+ propertyName: 'text',
172
+ propertyValue,
173
+ } );
174
+
175
+ // Assert
176
+ expect( Schema.validatePropValue ).toHaveBeenCalledWith(
177
+ expect.objectContaining( PROP_SCHEMA_ENTRY ),
178
+ propertyValue
179
+ );
180
+ expect( updateElementSettings ).toHaveBeenCalledWith( {
181
+ id: ELEMENT_ID,
182
+ props: {
183
+ [ PROPERTY_NAME ]: propertyValue,
184
+ },
185
+ withHistory: false,
186
+ } );
187
+ } );
188
+
146
189
  it( 'replaces existing local style custom_css by default', () => {
147
190
  // Arrange
148
191
  jest.mocked( getElementStyles ).mockReturnValue( {