@elementor/editor-canvas 4.2.0-924 → 4.2.0-926

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 CHANGED
@@ -2742,7 +2742,8 @@ function createTemplatedElementView({
2742
2742
  interaction_id: this.getInteractionId(),
2743
2743
  type,
2744
2744
  settings,
2745
- base_styles: baseStylesDictionary
2745
+ base_styles: baseStylesDictionary,
2746
+ ...this.getResolverRenderContext?.() ?? {}
2746
2747
  };
2747
2748
  return renderer.render(templateKey, context);
2748
2749
  }).then((html) => {
@@ -2906,7 +2907,8 @@ function createNestedTemplatedElementView({
2906
2907
  settings,
2907
2908
  base_styles: baseStylesDictionary,
2908
2909
  editor_attributes: buildEditorAttributes(model),
2909
- editor_classes: buildEditorClasses(model)
2910
+ editor_classes: buildEditorClasses(model),
2911
+ ...this.getResolverRenderContext?.() ?? {}
2910
2912
  };
2911
2913
  return renderer.render(templateKey, context);
2912
2914
  }).then((html) => {
@@ -3830,6 +3832,9 @@ function initTabsModelExtensions() {
3830
3832
  registerModelExtensions("e-tab", tabModelExtensions);
3831
3833
  }
3832
3834
 
3835
+ // src/mcp/canvas-mcp.ts
3836
+ var import_editor_props11 = require("@elementor/editor-props");
3837
+
3833
3838
  // src/mcp/resources/available-widgets-resource.ts
3834
3839
  var import_editor_v1_adapters15 = require("@elementor/editor-v1-adapters");
3835
3840
  var AVAILABLE_WIDGETS_URI = "elementor://context/available-widgets";
@@ -3979,8 +3984,109 @@ function extractElementData(element) {
3979
3984
  return result;
3980
3985
  }
3981
3986
 
3982
- // src/mcp/resources/editor-state-resource.ts
3987
+ // src/mcp/resources/dynamic-tags-resource.ts
3988
+ var import_editor_props7 = require("@elementor/editor-props");
3989
+
3990
+ // src/mcp/utils/resolve-dynamic-tag.ts
3983
3991
  var import_editor_v1_adapters17 = require("@elementor/editor-v1-adapters");
3992
+ var DYNAMIC_PROP_TYPE_KEY = "dynamic";
3993
+ var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
3994
+ var getAtomicDynamicTags = () => {
3995
+ const config = (0, import_editor_v1_adapters17.getElementorConfig)();
3996
+ return config.atomicDynamicTags?.tags ?? {};
3997
+ };
3998
+ var getDynamicTagNamesByCategories = (categories) => {
3999
+ if (!categories.length) {
4000
+ return [];
4001
+ }
4002
+ const wanted = new Set(categories);
4003
+ return Object.values(getAtomicDynamicTags()).filter((tag) => tag.categories?.some((category) => wanted.has(category))).map((tag) => tag.name);
4004
+ };
4005
+ var dynamicTagLLMResolver = (value) => {
4006
+ const input = value ?? {};
4007
+ const tag = input.name ? getAtomicDynamicTags()[input.name] : void 0;
4008
+ if (!tag) {
4009
+ return {
4010
+ $$type: DYNAMIC_PROP_TYPE_KEY,
4011
+ value: { name: input.name ?? "", group: "", settings: {} }
4012
+ };
4013
+ }
4014
+ return {
4015
+ $$type: DYNAMIC_PROP_TYPE_KEY,
4016
+ value: {
4017
+ name: tag.name,
4018
+ group: tag.group,
4019
+ settings: buildStrictSettings(tag.props_schema ?? {}, input.settings ?? {})
4020
+ }
4021
+ };
4022
+ };
4023
+ var buildStrictSettings = (schema2, provided) => {
4024
+ const settings = {};
4025
+ for (const [key, propType] of Object.entries(schema2)) {
4026
+ if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
4027
+ continue;
4028
+ }
4029
+ const resolved = provided[key] !== void 0 ? wrapSettingValue(provided[key], propType) : defaultSettingValue(propType);
4030
+ if (resolved !== void 0 && resolved !== null) {
4031
+ settings[key] = resolved;
4032
+ }
4033
+ }
4034
+ return settings;
4035
+ };
4036
+ var wrapSettingValue = (raw, propType) => {
4037
+ if (raw !== null && typeof raw === "object") {
4038
+ return raw;
4039
+ }
4040
+ return propType.key ? { $$type: propType.key, value: raw } : raw;
4041
+ };
4042
+ var defaultSettingValue = (propType) => {
4043
+ if (propType.initial_value !== null && propType.initial_value !== void 0) {
4044
+ return propType.initial_value;
4045
+ }
4046
+ if (propType.default !== null && propType.default !== void 0) {
4047
+ return wrapSettingValue(propType.default, propType);
4048
+ }
4049
+ return void 0;
4050
+ };
4051
+
4052
+ // src/mcp/resources/dynamic-tags-resource.ts
4053
+ var DYNAMIC_TAGS_URI = "elementor://dynamic-tags";
4054
+ var settingsSchema = (propsSchema) => {
4055
+ return Object.fromEntries(
4056
+ Object.entries(propsSchema ?? {}).filter(([key]) => !OMITTED_DYNAMIC_SETTING_KEYS.includes(key)).map(([key, propType]) => [key, import_editor_props7.Schema.propTypeToJsonSchema(propType)])
4057
+ );
4058
+ };
4059
+ var buildDynamicTagsList = () => {
4060
+ return Object.values(getAtomicDynamicTags()).map((tag) => ({
4061
+ name: tag.name,
4062
+ label: tag.label,
4063
+ categories: tag.categories,
4064
+ settings: settingsSchema(tag.props_schema)
4065
+ }));
4066
+ };
4067
+ var initDynamicTagsResource = (reg) => {
4068
+ const { resource } = reg;
4069
+ resource(
4070
+ "dynamic-tags",
4071
+ DYNAMIC_TAGS_URI,
4072
+ {
4073
+ description: `List of available dynamic tags. To bind a property to a dynamic source, set its value to { "$$type": "dynamic", "value": { "name": <tag name>, "settings": { ... } } } using a tag whose name appears in that property's allowed list, and populate "settings" per the tag entry here.`,
4074
+ mimeType: "application/json"
4075
+ },
4076
+ async (uri) => ({
4077
+ contents: [
4078
+ {
4079
+ uri: uri.href,
4080
+ mimeType: "application/json",
4081
+ text: JSON.stringify(buildDynamicTagsList())
4082
+ }
4083
+ ]
4084
+ })
4085
+ );
4086
+ };
4087
+
4088
+ // src/mcp/resources/editor-state-resource.ts
4089
+ var import_editor_v1_adapters18 = require("@elementor/editor-v1-adapters");
3984
4090
  var CURRENTLY_VIEWED_SCREEN = "The user is currently viewing the Elementor editor";
3985
4091
  var PAGE_CONTENT_CHARACTER_LIMIT = 500;
3986
4092
  var PREVIEW_TEXT_NODE_MIN_LENGTH = 2;
@@ -4001,8 +4107,8 @@ var initEditorStateResource = (reg) => {
4001
4107
  lastSerializedState = serialized;
4002
4108
  sendResourceUpdated({ uri: EDITOR_STATE_URI });
4003
4109
  };
4004
- (0, import_editor_v1_adapters17.__privateListenTo)(
4005
- [(0, import_editor_v1_adapters17.commandEndEvent)("editor/documents/switch"), (0, import_editor_v1_adapters17.commandEndEvent)("editor/documents/attach-preview")],
4110
+ (0, import_editor_v1_adapters18.__privateListenTo)(
4111
+ [(0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/switch"), (0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/attach-preview")],
4006
4112
  notifyIfChanged
4007
4113
  );
4008
4114
  lastSerializedState = JSON.stringify(buildState());
@@ -4076,7 +4182,7 @@ function getPageTitle() {
4076
4182
  }
4077
4183
 
4078
4184
  // src/mcp/resources/general-context-resource.ts
4079
- var import_editor_v1_adapters18 = require("@elementor/editor-v1-adapters");
4185
+ var import_editor_v1_adapters19 = require("@elementor/editor-v1-adapters");
4080
4186
  var GENERAL_CONTEXT_URI = "elementor://context/general";
4081
4187
  var initGeneralContextResource = (reg) => {
4082
4188
  const { resource, sendResourceUpdated } = reg;
@@ -4137,11 +4243,11 @@ var initGeneralContextResource = (reg) => {
4137
4243
  };
4138
4244
  }
4139
4245
  );
4140
- (0, import_editor_v1_adapters18.__privateListenTo)(
4246
+ (0, import_editor_v1_adapters19.__privateListenTo)(
4141
4247
  [
4142
- (0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/switch"),
4143
- (0, import_editor_v1_adapters18.commandEndEvent)("editor/documents/attach-preview"),
4144
- (0, import_editor_v1_adapters18.commandEndEvent)("document/elements/settings")
4248
+ (0, import_editor_v1_adapters19.commandEndEvent)("editor/documents/switch"),
4249
+ (0, import_editor_v1_adapters19.commandEndEvent)("editor/documents/attach-preview"),
4250
+ (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/settings")
4145
4251
  ],
4146
4252
  pushUpdateIfChanged
4147
4253
  );
@@ -4150,7 +4256,7 @@ var initGeneralContextResource = (reg) => {
4150
4256
 
4151
4257
  // src/mcp/resources/selected-element-resource.ts
4152
4258
  var import_editor_elements12 = require("@elementor/editor-elements");
4153
- var import_editor_v1_adapters19 = require("@elementor/editor-v1-adapters");
4259
+ var import_editor_v1_adapters20 = require("@elementor/editor-v1-adapters");
4154
4260
  var SELECTED_ELEMENT_URI = "elementor://context/selected-element";
4155
4261
  var initSelectedElementResource = (reg) => {
4156
4262
  const { resource, sendResourceUpdated } = reg;
@@ -4181,11 +4287,11 @@ var initSelectedElementResource = (reg) => {
4181
4287
  }
4182
4288
  publishIfChanged(readSelectionFromEditor());
4183
4289
  };
4184
- (0, import_editor_v1_adapters19.__privateListenTo)(
4290
+ (0, import_editor_v1_adapters20.__privateListenTo)(
4185
4291
  [
4186
- (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/select"),
4187
- (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/deselect-all"),
4188
- (0, import_editor_v1_adapters19.commandEndEvent)("document/elements/settings")
4292
+ (0, import_editor_v1_adapters20.commandEndEvent)("document/elements/select"),
4293
+ (0, import_editor_v1_adapters20.commandEndEvent)("document/elements/deselect-all"),
4294
+ (0, import_editor_v1_adapters20.commandEndEvent)("document/elements/settings")
4189
4295
  ],
4190
4296
  onCommand
4191
4297
  );
@@ -4303,9 +4409,9 @@ var import_editor_elements15 = require("@elementor/editor-elements");
4303
4409
 
4304
4410
  // src/mcp/utils/do-update-element-property.ts
4305
4411
  var import_editor_elements13 = require("@elementor/editor-elements");
4306
- var import_editor_props7 = require("@elementor/editor-props");
4412
+ var import_editor_props8 = require("@elementor/editor-props");
4307
4413
  var import_editor_styles5 = require("@elementor/editor-styles");
4308
- var import_editor_v1_adapters20 = require("@elementor/editor-v1-adapters");
4414
+ var import_editor_v1_adapters21 = require("@elementor/editor-v1-adapters");
4309
4415
 
4310
4416
  // src/mcp/utils/merge-custom-css.ts
4311
4417
  var CUSTOM_CSS_SEPARATOR = "\n";
@@ -4328,9 +4434,12 @@ var LOCAL_STYLE_META = {
4328
4434
  };
4329
4435
  function resolvePropValue(value, forceKey) {
4330
4436
  const Utils = window.elementorV2.editorVariables.Utils;
4331
- return import_editor_props7.Schema.adjustLlmPropValueSchema(value, {
4437
+ return import_editor_props8.Schema.adjustLlmPropValueSchema(value, {
4332
4438
  forceKey,
4333
- transformers: Utils.globalVariablesLLMResolvers
4439
+ transformers: {
4440
+ ...Utils.globalVariablesLLMResolvers,
4441
+ [DYNAMIC_PROP_TYPE_KEY]: dynamicTagLLMResolver
4442
+ }
4334
4443
  });
4335
4444
  }
4336
4445
  var doUpdateElementProperty = (params) => {
@@ -4380,7 +4489,7 @@ var doUpdateElementProperty = (params) => {
4380
4489
  }
4381
4490
  if (propertyRawSchema.kind === "plain") {
4382
4491
  if (typeof propertyMapValue[stylePropName] !== "object") {
4383
- const propUtil = (0, import_editor_props7.getPropSchemaFromCache)(propertyRawSchema.key);
4492
+ const propUtil = (0, import_editor_props8.getPropSchemaFromCache)(propertyRawSchema.key);
4384
4493
  if (propUtil) {
4385
4494
  const plainValue = propUtil.create(propertyMapValue[stylePropName]);
4386
4495
  propertyMapValue[stylePropName] = plainValue;
@@ -4433,7 +4542,7 @@ var doUpdateElementProperty = (params) => {
4433
4542
  }
4434
4543
  const propKey = elementPropSchema[propertyName].key;
4435
4544
  const value = resolvePropValue(propertyValue, propKey);
4436
- const { valid, jsonSchema } = import_editor_props7.Schema.validatePropValue(elementPropSchema[propertyName], propertyValue);
4545
+ const { valid, jsonSchema } = import_editor_props8.Schema.validatePropValue(elementPropSchema[propertyName], propertyValue);
4437
4546
  if (!valid) {
4438
4547
  throw new Error(
4439
4548
  `Invalid PropValue for elementId: ${elementId}. PropKey: ${propKey}, PropValue: ${JSON.stringify(
@@ -4449,12 +4558,12 @@ Expected Schema: ${jsonSchema}`
4449
4558
  },
4450
4559
  withHistory: false
4451
4560
  });
4452
- (0, import_editor_v1_adapters20.__privateRunCommandSync)("document/save/set-is-modified", { status: true }, { internal: true });
4561
+ (0, import_editor_v1_adapters21.__privateRunCommandSync)("document/save/set-is-modified", { status: true }, { internal: true });
4453
4562
  };
4454
4563
 
4455
4564
  // src/mcp/utils/validate-input.ts
4456
4565
  var import_editor_elements14 = require("@elementor/editor-elements");
4457
- var import_editor_props8 = require("@elementor/editor-props");
4566
+ var import_editor_props9 = require("@elementor/editor-props");
4458
4567
  var import_editor_styles6 = require("@elementor/editor-styles");
4459
4568
  var _widgetsSchema = null;
4460
4569
  var validateInput = {
@@ -4488,10 +4597,10 @@ var validateInput = {
4488
4597
  if (!propSchema) {
4489
4598
  errors.push(`Property "${propName}" is not defined in the schema.`);
4490
4599
  hasInvalidKey = true;
4491
- } else if (!import_editor_props8.Schema.isPropKeyConfigurable(propName, propSchema)) {
4600
+ } else if (!import_editor_props9.Schema.isPropKeyConfigurable(propName, propSchema)) {
4492
4601
  errors.push(`Property "${propName}" is not configurable.`);
4493
4602
  } else {
4494
- const { valid } = import_editor_props8.Schema.validatePropValue(propSchema, propValue);
4603
+ const { valid } = import_editor_props9.Schema.validatePropValue(propSchema, propValue);
4495
4604
  if (!valid) {
4496
4605
  errors.push(
4497
4606
  `Invalid property "${propName}". Validate input with resource [${STYLE_SCHEMA_URI.replace(
@@ -4875,6 +4984,13 @@ Some elements have internal tree structures (nesting). When using these elements
4875
4984
  - NO LINKS in configuration
4876
4985
  - Retry on errors up to 10x
4877
4986
 
4987
+ # DYNAMIC TAGS
4988
+ - A value can be made dynamic wherever its schema exposes a \`"$$type": "dynamic"\` variant. This may be the property root OR a NESTED field (e.g. an image's \`src\`, not the whole \`image\`).
4989
+ - Put the dynamic object EXACTLY at that node, in place of the static variant. The variant's \`name\` lists the allowed tags; read [${DYNAMIC_TAGS_URI}] for each tag's settings schema.
4990
+ - Provide at that node: \`{ "$$type": "dynamic", "value": { "name": "<allowed tag>", "settings": { ... } } }\`
4991
+ - Example (image): \`{ "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }\`
4992
+ - Do NOT send \`group\` (it is resolved automatically). Populate \`settings\` strictly per the tag's schema; use \`{}\` only when it has none.
4993
+
4878
4994
  Note about configuration ids: These names are visible to the end-user, make sure they make sense, related and relevant.
4879
4995
 
4880
4996
  # DESIGN PHILOSOPHY: CONTEXT-DRIVEN CREATIVITY
@@ -5096,7 +5212,8 @@ var initBuildCompositionsTool = (reg) => {
5096
5212
  { description: "Global Classes", uri: "elementor://global-classes" },
5097
5213
  { description: "Global Variables", uri: "elementor://global-variables" },
5098
5214
  { description: "Styles best practices", uri: BEST_PRACTICES_URI },
5099
- { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 }
5215
+ { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 },
5216
+ { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5100
5217
  ],
5101
5218
  outputSchema,
5102
5219
  handler: async (rawParams) => {
@@ -5261,6 +5378,21 @@ For styleProperties, use the style schema provided, as it also uses the PropType
5261
5378
  For all non-primitive types, provide the key property as defined in the schema as $$type in the generated object, as it is MANDATORY for parsing.
5262
5379
 
5263
5380
  Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property from the original configuration for every property you are changing.
5381
+
5382
+ # Dynamic tags
5383
+ A value can be made dynamic wherever its schema exposes a variant with "$$type": "dynamic". This may be the property root OR a NESTED field: for example an image is made dynamic on its "src" (the root stays "image"), NOT on the whole "image" value.
5384
+ Put the dynamic object EXACTLY at the node whose schema offers the "dynamic" variant, in place of the static variant. The variant's "name" enumerates the tags allowed at that node.
5385
+ 1. Read the [${DYNAMIC_TAGS_URI}] resource for each allowed tag's settings schema.
5386
+ 2. Provide, at that node:
5387
+ {
5388
+ "$$type": "dynamic",
5389
+ "value": {
5390
+ "name": "<allowed tag name>",
5391
+ "settings": { /* strictly per the tag's settings schema */ }
5392
+ }
5393
+ }
5394
+ Image example: { "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }
5395
+ Do NOT send "group" (it is resolved automatically). Use { "settings": {} } only when the tag has no settings.
5264
5396
  `);
5265
5397
  configureElementToolPrompt.parameter("elementId", "The ID of the element to configure. MANDATORY.");
5266
5398
  configureElementToolPrompt.parameter(
@@ -5368,7 +5500,8 @@ var initConfigureElementTool = (reg) => {
5368
5500
  requiredResources: [
5369
5501
  { description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
5370
5502
  { description: "Styles schema", uri: STYLE_SCHEMA_URI },
5371
- { description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI }
5503
+ { description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI },
5504
+ { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5372
5505
  ],
5373
5506
  handler: ({ elementId, propertiesToChange, elementType, stylePropertiesToChange }) => {
5374
5507
  const widgetData = (0, import_editor_elements17.getWidgetsCache)()?.[elementType];
@@ -5464,7 +5597,7 @@ Check the styles schema at the resource [${STYLE_SCHEMA_URI.replace(
5464
5597
 
5465
5598
  // src/mcp/tools/get-element-config/tool.ts
5466
5599
  var import_editor_elements18 = require("@elementor/editor-elements");
5467
- var import_editor_props9 = require("@elementor/editor-props");
5600
+ var import_editor_props10 = require("@elementor/editor-props");
5468
5601
  var import_schema5 = require("@elementor/schema");
5469
5602
  var schema = {
5470
5603
  elementId: import_schema5.z.string()
@@ -5521,7 +5654,7 @@ var initGetElementConfigTool = (reg) => {
5521
5654
  }
5522
5655
  const propValues = {};
5523
5656
  const stylePropValues = {};
5524
- import_editor_props9.Schema.configurableKeys(propSchema).forEach((key) => {
5657
+ import_editor_props10.Schema.configurableKeys(propSchema).forEach((key) => {
5525
5658
  propValues[key] = structuredClone(elementRawSettings.get(key));
5526
5659
  });
5527
5660
  const elementStyles = (0, import_editor_elements18.getElementStyles)(elementId) || {};
@@ -5557,9 +5690,11 @@ var initGetElementConfigTool = (reg) => {
5557
5690
 
5558
5691
  // src/mcp/canvas-mcp.ts
5559
5692
  var initCanvasMcp = (reg) => {
5693
+ import_editor_props11.Schema.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5560
5694
  initWidgetsSchemaResource(reg);
5561
5695
  initAvailableWidgetsResource(reg);
5562
5696
  initDocumentStructureResource(reg);
5697
+ initDynamicTagsResource(reg);
5563
5698
  initSelectedElementResource(reg);
5564
5699
  initEditorStateResource(reg);
5565
5700
  initGeneralContextResource(reg);
@@ -5684,14 +5819,14 @@ Note: The "size" property controls image resolution/loading, not visual size. Se
5684
5819
  // src/prevent-link-in-link-commands.ts
5685
5820
  var import_editor_elements19 = require("@elementor/editor-elements");
5686
5821
  var import_editor_notifications3 = require("@elementor/editor-notifications");
5687
- var import_editor_v1_adapters21 = require("@elementor/editor-v1-adapters");
5822
+ var import_editor_v1_adapters22 = require("@elementor/editor-v1-adapters");
5688
5823
  var import_i18n4 = require("@wordpress/i18n");
5689
5824
  function initLinkInLinkPrevention() {
5690
- (0, import_editor_v1_adapters21.blockCommand)({
5825
+ (0, import_editor_v1_adapters22.blockCommand)({
5691
5826
  command: "document/elements/paste",
5692
5827
  condition: blockLinkInLinkPaste
5693
5828
  });
5694
- (0, import_editor_v1_adapters21.blockCommand)({
5829
+ (0, import_editor_v1_adapters22.blockCommand)({
5695
5830
  command: "document/elements/move",
5696
5831
  condition: blockLinkInLinkMove
5697
5832
  });
@@ -5766,12 +5901,12 @@ function shouldBlock(sourceElements, targetElements) {
5766
5901
 
5767
5902
  // src/style-commands/paste-style.ts
5768
5903
  var import_editor_elements22 = require("@elementor/editor-elements");
5769
- var import_editor_props11 = require("@elementor/editor-props");
5770
- var import_editor_v1_adapters23 = require("@elementor/editor-v1-adapters");
5904
+ var import_editor_props13 = require("@elementor/editor-props");
5905
+ var import_editor_v1_adapters24 = require("@elementor/editor-v1-adapters");
5771
5906
 
5772
5907
  // src/utils/command-utils.ts
5773
5908
  var import_editor_elements20 = require("@elementor/editor-elements");
5774
- var import_editor_props10 = require("@elementor/editor-props");
5909
+ var import_editor_props12 = require("@elementor/editor-props");
5775
5910
  var import_i18n5 = require("@wordpress/i18n");
5776
5911
  function hasAtomicWidgets(args) {
5777
5912
  const { containers = [args.container] } = args;
@@ -5789,7 +5924,7 @@ function getClassesProp(container) {
5789
5924
  return null;
5790
5925
  }
5791
5926
  const [propKey] = Object.entries(propsSchema).find(
5792
- ([, propType]) => propType.kind === "plain" && propType.key === import_editor_props10.CLASSES_PROP_KEY
5927
+ ([, propType]) => propType.kind === "plain" && propType.key === import_editor_props12.CLASSES_PROP_KEY
5793
5928
  ) ?? [];
5794
5929
  return propKey ?? null;
5795
5930
  }
@@ -5814,9 +5949,9 @@ function getTitleForContainers(containers) {
5814
5949
  // src/style-commands/undoable-actions/paste-element-style.ts
5815
5950
  var import_editor_elements21 = require("@elementor/editor-elements");
5816
5951
  var import_editor_styles_repository4 = require("@elementor/editor-styles-repository");
5817
- var import_editor_v1_adapters22 = require("@elementor/editor-v1-adapters");
5952
+ var import_editor_v1_adapters23 = require("@elementor/editor-v1-adapters");
5818
5953
  var import_i18n6 = require("@wordpress/i18n");
5819
- var undoablePasteElementStyle = () => (0, import_editor_v1_adapters22.undoable)(
5954
+ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters23.undoable)(
5820
5955
  {
5821
5956
  do: ({ containers, newStyle }) => {
5822
5957
  return containers.map((container) => {
@@ -5892,12 +6027,12 @@ var undoablePasteElementStyle = () => (0, import_editor_v1_adapters22.undoable)(
5892
6027
  // src/style-commands/paste-style.ts
5893
6028
  function initPasteStyleCommand() {
5894
6029
  const pasteElementStyleCommand = undoablePasteElementStyle();
5895
- (0, import_editor_v1_adapters23.blockCommand)({
6030
+ (0, import_editor_v1_adapters24.blockCommand)({
5896
6031
  command: "document/elements/paste-style",
5897
6032
  condition: hasAtomicWidgets
5898
6033
  });
5899
- (0, import_editor_v1_adapters23.__privateListenTo)(
5900
- (0, import_editor_v1_adapters23.commandStartEvent)("document/elements/paste-style"),
6034
+ (0, import_editor_v1_adapters24.__privateListenTo)(
6035
+ (0, import_editor_v1_adapters24.commandStartEvent)("document/elements/paste-style"),
5901
6036
  (e) => pasteStyles(e.args, pasteElementStyleCommand)
5902
6037
  );
5903
6038
  }
@@ -5938,8 +6073,8 @@ function pasteClasses(containers, classes) {
5938
6073
  return;
5939
6074
  }
5940
6075
  const classesSetting = (0, import_editor_elements22.getElementSetting)(container.id, classesProp);
5941
- const currentClasses = import_editor_props11.classesPropTypeUtil.extract(classesSetting) ?? [];
5942
- const newClasses = import_editor_props11.classesPropTypeUtil.create(Array.from(/* @__PURE__ */ new Set([...classes, ...currentClasses])));
6076
+ const currentClasses = import_editor_props13.classesPropTypeUtil.extract(classesSetting) ?? [];
6077
+ const newClasses = import_editor_props13.classesPropTypeUtil.create(Array.from(/* @__PURE__ */ new Set([...classes, ...currentClasses])));
5943
6078
  (0, import_editor_elements22.updateElementSettings)({
5944
6079
  id: container.id,
5945
6080
  props: { [classesProp]: newClasses }
@@ -5948,14 +6083,14 @@ function pasteClasses(containers, classes) {
5948
6083
  }
5949
6084
 
5950
6085
  // src/style-commands/reset-style.ts
5951
- var import_editor_v1_adapters25 = require("@elementor/editor-v1-adapters");
6086
+ var import_editor_v1_adapters26 = require("@elementor/editor-v1-adapters");
5952
6087
 
5953
6088
  // src/style-commands/undoable-actions/reset-element-style.ts
5954
6089
  var import_editor_elements23 = require("@elementor/editor-elements");
5955
6090
  var import_editor_styles_repository5 = require("@elementor/editor-styles-repository");
5956
- var import_editor_v1_adapters24 = require("@elementor/editor-v1-adapters");
6091
+ var import_editor_v1_adapters25 = require("@elementor/editor-v1-adapters");
5957
6092
  var import_i18n7 = require("@wordpress/i18n");
5958
- var undoableResetElementStyle = () => (0, import_editor_v1_adapters24.undoable)(
6093
+ var undoableResetElementStyle = () => (0, import_editor_v1_adapters25.undoable)(
5959
6094
  {
5960
6095
  do: ({ containers }) => {
5961
6096
  return containers.map((container) => {
@@ -5999,12 +6134,12 @@ var undoableResetElementStyle = () => (0, import_editor_v1_adapters24.undoable)(
5999
6134
  // src/style-commands/reset-style.ts
6000
6135
  function initResetStyleCommand() {
6001
6136
  const resetElementStyles = undoableResetElementStyle();
6002
- (0, import_editor_v1_adapters25.blockCommand)({
6137
+ (0, import_editor_v1_adapters26.blockCommand)({
6003
6138
  command: "document/elements/reset-style",
6004
6139
  condition: hasAtomicWidgets
6005
6140
  });
6006
- (0, import_editor_v1_adapters25.__privateListenTo)(
6007
- (0, import_editor_v1_adapters25.commandStartEvent)("document/elements/reset-style"),
6141
+ (0, import_editor_v1_adapters26.__privateListenTo)(
6142
+ (0, import_editor_v1_adapters26.commandStartEvent)("document/elements/reset-style"),
6008
6143
  (e) => resetStyles(e.args, resetElementStyles)
6009
6144
  );
6010
6145
  }
@@ -6153,9 +6288,9 @@ function getRectClipPath(rect, viewport) {
6153
6288
  }
6154
6289
 
6155
6290
  // src/hooks/use-canvas-document.ts
6156
- var import_editor_v1_adapters26 = require("@elementor/editor-v1-adapters");
6291
+ var import_editor_v1_adapters27 = require("@elementor/editor-v1-adapters");
6157
6292
  function useCanvasDocument() {
6158
- return (0, import_editor_v1_adapters26.__privateUseListenTo)((0, import_editor_v1_adapters26.commandEndEvent)("editor/documents/attach-preview"), () => (0, import_editor_v1_adapters26.getCanvasIframeDocument)());
6293
+ return (0, import_editor_v1_adapters27.__privateUseListenTo)((0, import_editor_v1_adapters27.commandEndEvent)("editor/documents/attach-preview"), () => (0, import_editor_v1_adapters27.getCanvasIframeDocument)());
6159
6294
  }
6160
6295
 
6161
6296
  // src/hooks/use-escape-on-canvas.ts
package/dist/index.mjs CHANGED
@@ -2702,7 +2702,8 @@ function createTemplatedElementView({
2702
2702
  interaction_id: this.getInteractionId(),
2703
2703
  type,
2704
2704
  settings,
2705
- base_styles: baseStylesDictionary
2705
+ base_styles: baseStylesDictionary,
2706
+ ...this.getResolverRenderContext?.() ?? {}
2706
2707
  };
2707
2708
  return renderer.render(templateKey, context);
2708
2709
  }).then((html) => {
@@ -2866,7 +2867,8 @@ function createNestedTemplatedElementView({
2866
2867
  settings,
2867
2868
  base_styles: baseStylesDictionary,
2868
2869
  editor_attributes: buildEditorAttributes(model),
2869
- editor_classes: buildEditorClasses(model)
2870
+ editor_classes: buildEditorClasses(model),
2871
+ ...this.getResolverRenderContext?.() ?? {}
2870
2872
  };
2871
2873
  return renderer.render(templateKey, context);
2872
2874
  }).then((html) => {
@@ -3794,6 +3796,9 @@ function initTabsModelExtensions() {
3794
3796
  registerModelExtensions("e-tab", tabModelExtensions);
3795
3797
  }
3796
3798
 
3799
+ // src/mcp/canvas-mcp.ts
3800
+ import { Schema as Schema6 } from "@elementor/editor-props";
3801
+
3797
3802
  // src/mcp/resources/available-widgets-resource.ts
3798
3803
  import { v1ReadyEvent as v1ReadyEvent3 } from "@elementor/editor-v1-adapters";
3799
3804
  var AVAILABLE_WIDGETS_URI = "elementor://context/available-widgets";
@@ -3945,6 +3950,107 @@ function extractElementData(element) {
3945
3950
  return result;
3946
3951
  }
3947
3952
 
3953
+ // src/mcp/resources/dynamic-tags-resource.ts
3954
+ import { Schema as Schema2 } from "@elementor/editor-props";
3955
+
3956
+ // src/mcp/utils/resolve-dynamic-tag.ts
3957
+ import { getElementorConfig } from "@elementor/editor-v1-adapters";
3958
+ var DYNAMIC_PROP_TYPE_KEY = "dynamic";
3959
+ var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
3960
+ var getAtomicDynamicTags = () => {
3961
+ const config = getElementorConfig();
3962
+ return config.atomicDynamicTags?.tags ?? {};
3963
+ };
3964
+ var getDynamicTagNamesByCategories = (categories) => {
3965
+ if (!categories.length) {
3966
+ return [];
3967
+ }
3968
+ const wanted = new Set(categories);
3969
+ return Object.values(getAtomicDynamicTags()).filter((tag) => tag.categories?.some((category) => wanted.has(category))).map((tag) => tag.name);
3970
+ };
3971
+ var dynamicTagLLMResolver = (value) => {
3972
+ const input = value ?? {};
3973
+ const tag = input.name ? getAtomicDynamicTags()[input.name] : void 0;
3974
+ if (!tag) {
3975
+ return {
3976
+ $$type: DYNAMIC_PROP_TYPE_KEY,
3977
+ value: { name: input.name ?? "", group: "", settings: {} }
3978
+ };
3979
+ }
3980
+ return {
3981
+ $$type: DYNAMIC_PROP_TYPE_KEY,
3982
+ value: {
3983
+ name: tag.name,
3984
+ group: tag.group,
3985
+ settings: buildStrictSettings(tag.props_schema ?? {}, input.settings ?? {})
3986
+ }
3987
+ };
3988
+ };
3989
+ var buildStrictSettings = (schema2, provided) => {
3990
+ const settings = {};
3991
+ for (const [key, propType] of Object.entries(schema2)) {
3992
+ if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
3993
+ continue;
3994
+ }
3995
+ const resolved = provided[key] !== void 0 ? wrapSettingValue(provided[key], propType) : defaultSettingValue(propType);
3996
+ if (resolved !== void 0 && resolved !== null) {
3997
+ settings[key] = resolved;
3998
+ }
3999
+ }
4000
+ return settings;
4001
+ };
4002
+ var wrapSettingValue = (raw, propType) => {
4003
+ if (raw !== null && typeof raw === "object") {
4004
+ return raw;
4005
+ }
4006
+ return propType.key ? { $$type: propType.key, value: raw } : raw;
4007
+ };
4008
+ var defaultSettingValue = (propType) => {
4009
+ if (propType.initial_value !== null && propType.initial_value !== void 0) {
4010
+ return propType.initial_value;
4011
+ }
4012
+ if (propType.default !== null && propType.default !== void 0) {
4013
+ return wrapSettingValue(propType.default, propType);
4014
+ }
4015
+ return void 0;
4016
+ };
4017
+
4018
+ // src/mcp/resources/dynamic-tags-resource.ts
4019
+ var DYNAMIC_TAGS_URI = "elementor://dynamic-tags";
4020
+ var settingsSchema = (propsSchema) => {
4021
+ return Object.fromEntries(
4022
+ Object.entries(propsSchema ?? {}).filter(([key]) => !OMITTED_DYNAMIC_SETTING_KEYS.includes(key)).map(([key, propType]) => [key, Schema2.propTypeToJsonSchema(propType)])
4023
+ );
4024
+ };
4025
+ var buildDynamicTagsList = () => {
4026
+ return Object.values(getAtomicDynamicTags()).map((tag) => ({
4027
+ name: tag.name,
4028
+ label: tag.label,
4029
+ categories: tag.categories,
4030
+ settings: settingsSchema(tag.props_schema)
4031
+ }));
4032
+ };
4033
+ var initDynamicTagsResource = (reg) => {
4034
+ const { resource } = reg;
4035
+ resource(
4036
+ "dynamic-tags",
4037
+ DYNAMIC_TAGS_URI,
4038
+ {
4039
+ description: `List of available dynamic tags. To bind a property to a dynamic source, set its value to { "$$type": "dynamic", "value": { "name": <tag name>, "settings": { ... } } } using a tag whose name appears in that property's allowed list, and populate "settings" per the tag entry here.`,
4040
+ mimeType: "application/json"
4041
+ },
4042
+ async (uri) => ({
4043
+ contents: [
4044
+ {
4045
+ uri: uri.href,
4046
+ mimeType: "application/json",
4047
+ text: JSON.stringify(buildDynamicTagsList())
4048
+ }
4049
+ ]
4050
+ })
4051
+ );
4052
+ };
4053
+
3948
4054
  // src/mcp/resources/editor-state-resource.ts
3949
4055
  import { __privateListenTo as listenTo2, commandEndEvent as commandEndEvent5 } from "@elementor/editor-v1-adapters";
3950
4056
  var CURRENTLY_VIEWED_SCREEN = "The user is currently viewing the Elementor editor";
@@ -4289,7 +4395,7 @@ import {
4289
4395
  updateElementSettings,
4290
4396
  updateElementStyle
4291
4397
  } from "@elementor/editor-elements";
4292
- import { getPropSchemaFromCache, Schema as Schema2 } from "@elementor/editor-props";
4398
+ import { getPropSchemaFromCache, Schema as Schema3 } from "@elementor/editor-props";
4293
4399
  import { getStylesSchema as getStylesSchema3, getVariantByMeta } from "@elementor/editor-styles";
4294
4400
  import { __privateRunCommandSync as runCommandSync2 } from "@elementor/editor-v1-adapters";
4295
4401
 
@@ -4314,9 +4420,12 @@ var LOCAL_STYLE_META = {
4314
4420
  };
4315
4421
  function resolvePropValue(value, forceKey) {
4316
4422
  const Utils = window.elementorV2.editorVariables.Utils;
4317
- return Schema2.adjustLlmPropValueSchema(value, {
4423
+ return Schema3.adjustLlmPropValueSchema(value, {
4318
4424
  forceKey,
4319
- transformers: Utils.globalVariablesLLMResolvers
4425
+ transformers: {
4426
+ ...Utils.globalVariablesLLMResolvers,
4427
+ [DYNAMIC_PROP_TYPE_KEY]: dynamicTagLLMResolver
4428
+ }
4320
4429
  });
4321
4430
  }
4322
4431
  var doUpdateElementProperty = (params) => {
@@ -4419,7 +4528,7 @@ var doUpdateElementProperty = (params) => {
4419
4528
  }
4420
4529
  const propKey = elementPropSchema[propertyName].key;
4421
4530
  const value = resolvePropValue(propertyValue, propKey);
4422
- const { valid, jsonSchema } = Schema2.validatePropValue(elementPropSchema[propertyName], propertyValue);
4531
+ const { valid, jsonSchema } = Schema3.validatePropValue(elementPropSchema[propertyName], propertyValue);
4423
4532
  if (!valid) {
4424
4533
  throw new Error(
4425
4534
  `Invalid PropValue for elementId: ${elementId}. PropKey: ${propKey}, PropValue: ${JSON.stringify(
@@ -4440,7 +4549,7 @@ Expected Schema: ${jsonSchema}`
4440
4549
 
4441
4550
  // src/mcp/utils/validate-input.ts
4442
4551
  import { getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
4443
- import { Schema as Schema3 } from "@elementor/editor-props";
4552
+ import { Schema as Schema4 } from "@elementor/editor-props";
4444
4553
  import { getStylesSchema as getStylesSchema4 } from "@elementor/editor-styles";
4445
4554
  var _widgetsSchema = null;
4446
4555
  var validateInput = {
@@ -4474,10 +4583,10 @@ var validateInput = {
4474
4583
  if (!propSchema) {
4475
4584
  errors.push(`Property "${propName}" is not defined in the schema.`);
4476
4585
  hasInvalidKey = true;
4477
- } else if (!Schema3.isPropKeyConfigurable(propName, propSchema)) {
4586
+ } else if (!Schema4.isPropKeyConfigurable(propName, propSchema)) {
4478
4587
  errors.push(`Property "${propName}" is not configurable.`);
4479
4588
  } else {
4480
- const { valid } = Schema3.validatePropValue(propSchema, propValue);
4589
+ const { valid } = Schema4.validatePropValue(propSchema, propValue);
4481
4590
  if (!valid) {
4482
4591
  errors.push(
4483
4592
  `Invalid property "${propName}". Validate input with resource [${STYLE_SCHEMA_URI.replace(
@@ -4861,6 +4970,13 @@ Some elements have internal tree structures (nesting). When using these elements
4861
4970
  - NO LINKS in configuration
4862
4971
  - Retry on errors up to 10x
4863
4972
 
4973
+ # DYNAMIC TAGS
4974
+ - A value can be made dynamic wherever its schema exposes a \`"$$type": "dynamic"\` variant. This may be the property root OR a NESTED field (e.g. an image's \`src\`, not the whole \`image\`).
4975
+ - Put the dynamic object EXACTLY at that node, in place of the static variant. The variant's \`name\` lists the allowed tags; read [${DYNAMIC_TAGS_URI}] for each tag's settings schema.
4976
+ - Provide at that node: \`{ "$$type": "dynamic", "value": { "name": "<allowed tag>", "settings": { ... } } }\`
4977
+ - Example (image): \`{ "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }\`
4978
+ - Do NOT send \`group\` (it is resolved automatically). Populate \`settings\` strictly per the tag's schema; use \`{}\` only when it has none.
4979
+
4864
4980
  Note about configuration ids: These names are visible to the end-user, make sure they make sense, related and relevant.
4865
4981
 
4866
4982
  # DESIGN PHILOSOPHY: CONTEXT-DRIVEN CREATIVITY
@@ -5082,7 +5198,8 @@ var initBuildCompositionsTool = (reg) => {
5082
5198
  { description: "Global Classes", uri: "elementor://global-classes" },
5083
5199
  { description: "Global Variables", uri: "elementor://global-variables" },
5084
5200
  { description: "Styles best practices", uri: BEST_PRACTICES_URI },
5085
- { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 }
5201
+ { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 },
5202
+ { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5086
5203
  ],
5087
5204
  outputSchema,
5088
5205
  handler: async (rawParams) => {
@@ -5247,6 +5364,21 @@ For styleProperties, use the style schema provided, as it also uses the PropType
5247
5364
  For all non-primitive types, provide the key property as defined in the schema as $$type in the generated object, as it is MANDATORY for parsing.
5248
5365
 
5249
5366
  Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property from the original configuration for every property you are changing.
5367
+
5368
+ # Dynamic tags
5369
+ A value can be made dynamic wherever its schema exposes a variant with "$$type": "dynamic". This may be the property root OR a NESTED field: for example an image is made dynamic on its "src" (the root stays "image"), NOT on the whole "image" value.
5370
+ Put the dynamic object EXACTLY at the node whose schema offers the "dynamic" variant, in place of the static variant. The variant's "name" enumerates the tags allowed at that node.
5371
+ 1. Read the [${DYNAMIC_TAGS_URI}] resource for each allowed tag's settings schema.
5372
+ 2. Provide, at that node:
5373
+ {
5374
+ "$$type": "dynamic",
5375
+ "value": {
5376
+ "name": "<allowed tag name>",
5377
+ "settings": { /* strictly per the tag's settings schema */ }
5378
+ }
5379
+ }
5380
+ Image example: { "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }
5381
+ Do NOT send "group" (it is resolved automatically). Use { "settings": {} } only when the tag has no settings.
5250
5382
  `);
5251
5383
  configureElementToolPrompt.parameter("elementId", "The ID of the element to configure. MANDATORY.");
5252
5384
  configureElementToolPrompt.parameter(
@@ -5354,7 +5486,8 @@ var initConfigureElementTool = (reg) => {
5354
5486
  requiredResources: [
5355
5487
  { description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
5356
5488
  { description: "Styles schema", uri: STYLE_SCHEMA_URI },
5357
- { description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI }
5489
+ { description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI },
5490
+ { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5358
5491
  ],
5359
5492
  handler: ({ elementId, propertiesToChange, elementType, stylePropertiesToChange }) => {
5360
5493
  const widgetData = getWidgetsCache10()?.[elementType];
@@ -5450,7 +5583,7 @@ Check the styles schema at the resource [${STYLE_SCHEMA_URI.replace(
5450
5583
 
5451
5584
  // src/mcp/tools/get-element-config/tool.ts
5452
5585
  import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache11 } from "@elementor/editor-elements";
5453
- import { Schema as Schema4 } from "@elementor/editor-props";
5586
+ import { Schema as Schema5 } from "@elementor/editor-props";
5454
5587
  import { z as z3 } from "@elementor/schema";
5455
5588
  var schema = {
5456
5589
  elementId: z3.string()
@@ -5507,7 +5640,7 @@ var initGetElementConfigTool = (reg) => {
5507
5640
  }
5508
5641
  const propValues = {};
5509
5642
  const stylePropValues = {};
5510
- Schema4.configurableKeys(propSchema).forEach((key) => {
5643
+ Schema5.configurableKeys(propSchema).forEach((key) => {
5511
5644
  propValues[key] = structuredClone(elementRawSettings.get(key));
5512
5645
  });
5513
5646
  const elementStyles = getElementStyles2(elementId) || {};
@@ -5543,9 +5676,11 @@ var initGetElementConfigTool = (reg) => {
5543
5676
 
5544
5677
  // src/mcp/canvas-mcp.ts
5545
5678
  var initCanvasMcp = (reg) => {
5679
+ Schema6.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5546
5680
  initWidgetsSchemaResource(reg);
5547
5681
  initAvailableWidgetsResource(reg);
5548
5682
  initDocumentStructureResource(reg);
5683
+ initDynamicTagsResource(reg);
5549
5684
  initSelectedElementResource(reg);
5550
5685
  initEditorStateResource(reg);
5551
5686
  initGeneralContextResource(reg);
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-924",
4
+ "version": "4.2.0-926",
5
5
  "private": false,
6
6
  "author": "Elementor Team",
7
7
  "homepage": "https://elementor.com/",
@@ -37,25 +37,25 @@
37
37
  "react-dom": "^18.3.1"
38
38
  },
39
39
  "dependencies": {
40
- "@elementor/editor": "4.2.0-924",
40
+ "@elementor/editor": "4.2.0-926",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.2.0-924",
43
- "@elementor/editor-documents": "4.2.0-924",
44
- "@elementor/editor-elements": "4.2.0-924",
45
- "@elementor/editor-interactions": "4.2.0-924",
46
- "@elementor/editor-mcp": "4.2.0-924",
47
- "@elementor/editor-notifications": "4.2.0-924",
48
- "@elementor/editor-props": "4.2.0-924",
49
- "@elementor/editor-responsive": "4.2.0-924",
50
- "@elementor/editor-styles": "4.2.0-924",
51
- "@elementor/editor-styles-repository": "4.2.0-924",
52
- "@elementor/editor-ui": "4.2.0-924",
53
- "@elementor/editor-v1-adapters": "4.2.0-924",
54
- "@elementor/schema": "4.2.0-924",
55
- "@elementor/twing": "4.2.0-924",
42
+ "@elementor/editor-controls": "4.2.0-926",
43
+ "@elementor/editor-documents": "4.2.0-926",
44
+ "@elementor/editor-elements": "4.2.0-926",
45
+ "@elementor/editor-interactions": "4.2.0-926",
46
+ "@elementor/editor-mcp": "4.2.0-926",
47
+ "@elementor/editor-notifications": "4.2.0-926",
48
+ "@elementor/editor-props": "4.2.0-926",
49
+ "@elementor/editor-responsive": "4.2.0-926",
50
+ "@elementor/editor-styles": "4.2.0-926",
51
+ "@elementor/editor-styles-repository": "4.2.0-926",
52
+ "@elementor/editor-ui": "4.2.0-926",
53
+ "@elementor/editor-v1-adapters": "4.2.0-926",
54
+ "@elementor/schema": "4.2.0-926",
55
+ "@elementor/twing": "4.2.0-926",
56
56
  "@elementor/ui": "1.37.5",
57
- "@elementor/utils": "4.2.0-924",
58
- "@elementor/wp-media": "4.2.0-924",
57
+ "@elementor/utils": "4.2.0-926",
58
+ "@elementor/wp-media": "4.2.0-926",
59
59
  "@floating-ui/react": "^0.27.5",
60
60
  "@wordpress/i18n": "^5.13.0"
61
61
  },
@@ -200,6 +200,7 @@ export function createNestedTemplatedElementView( {
200
200
  base_styles: baseStylesDictionary,
201
201
  editor_attributes: buildEditorAttributes( model ),
202
202
  editor_classes: buildEditorClasses( model ),
203
+ ...( this.getResolverRenderContext?.() ?? {} ),
203
204
  };
204
205
 
205
206
  return renderer.render( templateKey, context );
@@ -166,6 +166,7 @@ export function createTemplatedElementView( {
166
166
  type,
167
167
  settings,
168
168
  base_styles: baseStylesDictionary,
169
+ ...( this.getResolverRenderContext?.() ?? {} ),
169
170
  };
170
171
 
171
172
  return renderer.render( templateKey, context );
@@ -1,8 +1,10 @@
1
1
  import { type MCPRegistryEntry } from '@elementor/editor-mcp';
2
+ import { Schema } from '@elementor/editor-props';
2
3
 
3
4
  import { initAvailableWidgetsResource } from './resources/available-widgets-resource';
4
5
  import { initBreakpointsResource } from './resources/breakpoints-resource';
5
6
  import { initDocumentStructureResource } from './resources/document-structure-resource';
7
+ import { initDynamicTagsResource } from './resources/dynamic-tags-resource';
6
8
  import { initEditorStateResource } from './resources/editor-state-resource';
7
9
  import { initGeneralContextResource } from './resources/general-context-resource';
8
10
  import { initSelectedElementResource } from './resources/selected-element-resource';
@@ -10,11 +12,14 @@ import { initWidgetsSchemaResource } from './resources/widgets-schema-resource';
10
12
  import { initBuildCompositionsTool } from './tools/build-composition/tool';
11
13
  import { initConfigureElementTool } from './tools/configure-element/tool';
12
14
  import { initGetElementConfigTool } from './tools/get-element-config/tool';
15
+ import { getDynamicTagNamesByCategories } from './utils/resolve-dynamic-tag';
13
16
 
14
17
  export const initCanvasMcp = ( reg: MCPRegistryEntry ) => {
18
+ Schema.setDynamicTagNamesResolver( getDynamicTagNamesByCategories );
15
19
  initWidgetsSchemaResource( reg );
16
20
  initAvailableWidgetsResource( reg );
17
21
  initDocumentStructureResource( reg );
22
+ initDynamicTagsResource( reg );
18
23
  initSelectedElementResource( reg );
19
24
  initEditorStateResource( reg );
20
25
  initGeneralContextResource( reg );
@@ -0,0 +1,85 @@
1
+ import { Schema } from '@elementor/editor-props';
2
+ import { getElementorConfig } from '@elementor/editor-v1-adapters';
3
+
4
+ import { DYNAMIC_TAGS_URI, initDynamicTagsResource } from '../dynamic-tags-resource';
5
+
6
+ jest.mock( '@elementor/editor-props', () => ( {
7
+ Schema: {
8
+ propTypeToJsonSchema: jest.fn( () => ( { mocked: true } ) ),
9
+ },
10
+ } ) );
11
+
12
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
13
+ getElementorConfig: jest.fn(),
14
+ } ) );
15
+
16
+ const mockedGetElementorConfig = getElementorConfig as jest.MockedFunction< typeof getElementorConfig >;
17
+
18
+ type ResourceHandler = ( uri: URL ) => Promise< { contents: { text: string }[] } >;
19
+
20
+ const captureHandler = (): ResourceHandler => {
21
+ const resource = jest.fn();
22
+ initDynamicTagsResource( { resource } as never );
23
+ return resource.mock.calls[ 0 ][ 3 ] as ResourceHandler;
24
+ };
25
+
26
+ const readCatalog = async () => {
27
+ const handler = captureHandler();
28
+ const result = await handler( new URL( DYNAMIC_TAGS_URI ) );
29
+ return JSON.parse( result.contents[ 0 ].text );
30
+ };
31
+
32
+ describe( 'dynamic-tags-resource', () => {
33
+ beforeEach( () => {
34
+ jest.clearAllMocks();
35
+ } );
36
+
37
+ it( 'returns a flat tag list with categories and omits the fallback setting', async () => {
38
+ // Arrange
39
+ mockedGetElementorConfig.mockReturnValue( {
40
+ atomicDynamicTags: {
41
+ tags: {
42
+ 'post-custom-field': {
43
+ name: 'post-custom-field',
44
+ label: 'Post Custom Field',
45
+ group: 'post',
46
+ categories: [ 'text', 'url' ],
47
+ props_schema: {
48
+ key: { kind: 'string', key: 'string' },
49
+ before: { kind: 'string', key: 'string' },
50
+ fallback: { kind: 'string', key: 'string' },
51
+ },
52
+ },
53
+ },
54
+ groups: { post: { title: 'Post' } },
55
+ },
56
+ } as never );
57
+
58
+ // Act
59
+ const catalog = await readCatalog();
60
+
61
+ // Assert
62
+ expect( Array.isArray( catalog ) ).toBe( true );
63
+ expect( catalog ).toHaveLength( 1 );
64
+ expect( catalog[ 0 ] ).toMatchObject( {
65
+ name: 'post-custom-field',
66
+ label: 'Post Custom Field',
67
+ categories: [ 'text', 'url' ],
68
+ } );
69
+ expect( catalog[ 0 ] ).not.toHaveProperty( 'group' );
70
+ expect( Object.keys( catalog[ 0 ].settings ) ).toEqual( [ 'key', 'before' ] );
71
+ expect( catalog[ 0 ].settings ).not.toHaveProperty( 'fallback' );
72
+ } );
73
+
74
+ it( 'returns an empty list when dynamic tags are unavailable', async () => {
75
+ // Arrange
76
+ mockedGetElementorConfig.mockReturnValue( {} as never );
77
+
78
+ // Act
79
+ const catalog = await readCatalog();
80
+
81
+ // Assert
82
+ expect( catalog ).toEqual( [] );
83
+ expect( Schema.propTypeToJsonSchema ).not.toHaveBeenCalled();
84
+ } );
85
+ } );
@@ -0,0 +1,52 @@
1
+ import { type MCPRegistryEntry } from '@elementor/editor-mcp';
2
+ import { type PropType, Schema } from '@elementor/editor-props';
3
+
4
+ import {
5
+ type AtomicDynamicTag,
6
+ getAtomicDynamicTags,
7
+ OMITTED_DYNAMIC_SETTING_KEYS,
8
+ } from '../utils/resolve-dynamic-tag';
9
+
10
+ export const DYNAMIC_TAGS_URI = 'elementor://dynamic-tags';
11
+
12
+ const settingsSchema = ( propsSchema: AtomicDynamicTag[ 'props_schema' ] ): Record< string, unknown > => {
13
+ return Object.fromEntries(
14
+ Object.entries( propsSchema ?? {} )
15
+ .filter( ( [ key ] ) => ! ( OMITTED_DYNAMIC_SETTING_KEYS as readonly string[] ).includes( key ) )
16
+ .map( ( [ key, propType ] ) => [ key, Schema.propTypeToJsonSchema( propType as PropType ) ] )
17
+ );
18
+ };
19
+
20
+ const buildDynamicTagsList = () => {
21
+ return Object.values( getAtomicDynamicTags() ).map( ( tag ) => ( {
22
+ name: tag.name,
23
+ label: tag.label,
24
+ categories: tag.categories,
25
+ settings: settingsSchema( tag.props_schema ),
26
+ } ) );
27
+ };
28
+
29
+ export const initDynamicTagsResource = ( reg: MCPRegistryEntry ) => {
30
+ const { resource } = reg;
31
+
32
+ resource(
33
+ 'dynamic-tags',
34
+ DYNAMIC_TAGS_URI,
35
+ {
36
+ description:
37
+ 'List of available dynamic tags. To bind a property to a dynamic source, set its value to ' +
38
+ '{ "$$type": "dynamic", "value": { "name": <tag name>, "settings": { ... } } } using a tag whose ' +
39
+ 'name appears in that property\'s allowed list, and populate "settings" per the tag entry here.',
40
+ mimeType: 'application/json',
41
+ },
42
+ async ( uri: URL ) => ( {
43
+ contents: [
44
+ {
45
+ uri: uri.href,
46
+ mimeType: 'application/json',
47
+ text: JSON.stringify( buildDynamicTagsList() ),
48
+ },
49
+ ],
50
+ } )
51
+ );
52
+ };
@@ -1,6 +1,7 @@
1
1
  import { toolPrompts } from '@elementor/editor-mcp';
2
2
 
3
3
  import { AVAILABLE_WIDGETS_URI } from '../../resources/available-widgets-resource';
4
+ import { DYNAMIC_TAGS_URI } from '../../resources/dynamic-tags-resource';
4
5
 
5
6
  export const BUILD_COMPOSITIONS_GUIDE_URI = 'elementor://canvas/tools/build-compositions-guide';
6
7
 
@@ -40,6 +41,13 @@ Some elements have internal tree structures (nesting). When using these elements
40
41
  - NO LINKS in configuration
41
42
  - Retry on errors up to 10x
42
43
 
44
+ # DYNAMIC TAGS
45
+ - A value can be made dynamic wherever its schema exposes a \`"$$type": "dynamic"\` variant. This may be the property root OR a NESTED field (e.g. an image's \`src\`, not the whole \`image\`).
46
+ - Put the dynamic object EXACTLY at that node, in place of the static variant. The variant's \`name\` lists the allowed tags; read [${ DYNAMIC_TAGS_URI }] for each tag's settings schema.
47
+ - Provide at that node: \`{ "$$type": "dynamic", "value": { "name": "<allowed tag>", "settings": { ... } } }\`
48
+ - Example (image): \`{ "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }\`
49
+ - Do NOT send \`group\` (it is resolved automatically). Populate \`settings\` strictly per the tag's schema; use \`{}\` only when it has none.
50
+
43
51
  Note about configuration ids: These names are visible to the end-user, make sure they make sense, related and relevant.
44
52
 
45
53
  # DESIGN PHILOSOPHY: CONTEXT-DRIVEN CREATIVITY
@@ -10,6 +10,7 @@ import { type MCPRegistryEntry } from '@elementor/editor-mcp';
10
10
 
11
11
  import { CompositionBuilder } from '../../../composition-builder/composition-builder';
12
12
  import { AVAILABLE_WIDGETS_URI_V4 } from '../../resources/available-widgets-resource';
13
+ import { DYNAMIC_TAGS_URI } from '../../resources/dynamic-tags-resource';
13
14
  import { BEST_PRACTICES_URI, STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
14
15
  import { isWidgetAvailableForLLM } from '../../utils/element-data-util';
15
16
  import { getCompositionTargetContainer } from '../../utils/get-composition-target-container';
@@ -45,6 +46,7 @@ export const initBuildCompositionsTool = ( reg: MCPRegistryEntry ) => {
45
46
  { description: 'Global Variables', uri: 'elementor://global-variables' },
46
47
  { description: 'Styles best practices', uri: BEST_PRACTICES_URI },
47
48
  { description: 'Available widgets for this tool', uri: AVAILABLE_WIDGETS_URI_V4 },
49
+ { description: 'Dynamic tags catalog', uri: DYNAMIC_TAGS_URI },
48
50
  ],
49
51
  outputSchema,
50
52
  handler: async ( rawParams ) => {
@@ -1,5 +1,6 @@
1
1
  import { toolPrompts } from '@elementor/editor-mcp';
2
2
 
3
+ import { DYNAMIC_TAGS_URI } from '../../resources/dynamic-tags-resource';
3
4
  import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
4
5
 
5
6
  export const CONFIGURE_ELEMENT_GUIDE_URI = 'elementor://canvas/tools/configure-element-guide';
@@ -59,6 +60,21 @@ For styleProperties, use the style schema provided, as it also uses the PropType
59
60
  For all non-primitive types, provide the key property as defined in the schema as $$type in the generated object, as it is MANDATORY for parsing.
60
61
 
61
62
  Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property from the original configuration for every property you are changing.
63
+
64
+ # Dynamic tags
65
+ A value can be made dynamic wherever its schema exposes a variant with "$$type": "dynamic". This may be the property root OR a NESTED field: for example an image is made dynamic on its "src" (the root stays "image"), NOT on the whole "image" value.
66
+ Put the dynamic object EXACTLY at the node whose schema offers the "dynamic" variant, in place of the static variant. The variant's "name" enumerates the tags allowed at that node.
67
+ 1. Read the [${ DYNAMIC_TAGS_URI }] resource for each allowed tag's settings schema.
68
+ 2. Provide, at that node:
69
+ {
70
+ "$$type": "dynamic",
71
+ "value": {
72
+ "name": "<allowed tag name>",
73
+ "settings": { /* strictly per the tag's settings schema */ }
74
+ }
75
+ }
76
+ Image example: { "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }
77
+ Do NOT send "group" (it is resolved automatically). Use { "settings": {} } only when the tag has no settings.
62
78
  ` );
63
79
 
64
80
  configureElementToolPrompt.parameter( 'elementId', 'The ID of the element to configure. MANDATORY.' );
@@ -1,6 +1,7 @@
1
1
  import { getWidgetsCache } from '@elementor/editor-elements';
2
2
  import { type MCPRegistryEntry } from '@elementor/editor-mcp';
3
3
 
4
+ import { DYNAMIC_TAGS_URI } from '../../resources/dynamic-tags-resource';
4
5
  import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
5
6
  import { doUpdateElementProperty } from '../../utils/do-update-element-property';
6
7
  import { validateInput } from '../../utils/validate-input';
@@ -32,6 +33,7 @@ export const initConfigureElementTool = ( reg: MCPRegistryEntry ) => {
32
33
  { description: 'Widgets schema', uri: WIDGET_SCHEMA_URI },
33
34
  { description: 'Styles schema', uri: STYLE_SCHEMA_URI },
34
35
  { description: 'Configure element guide', uri: CONFIGURE_ELEMENT_GUIDE_URI },
36
+ { description: 'Dynamic tags catalog', uri: DYNAMIC_TAGS_URI },
35
37
  ],
36
38
  handler: ( { elementId, propertiesToChange, elementType, stylePropertiesToChange } ) => {
37
39
  const widgetData = getWidgetsCache()?.[ elementType ];
@@ -0,0 +1,102 @@
1
+ import { getElementorConfig } from '@elementor/editor-v1-adapters';
2
+
3
+ import { dynamicTagLLMResolver, getDynamicTagNamesByCategories } from '../resolve-dynamic-tag';
4
+
5
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
6
+ getElementorConfig: jest.fn(),
7
+ } ) );
8
+
9
+ const mockedGetElementorConfig = getElementorConfig as jest.MockedFunction< typeof getElementorConfig >;
10
+
11
+ const givenTags = () =>
12
+ mockedGetElementorConfig.mockReturnValue( {
13
+ atomicDynamicTags: {
14
+ tags: {
15
+ 'post-custom-field': {
16
+ name: 'post-custom-field',
17
+ label: 'Post Custom Field',
18
+ group: 'post',
19
+ categories: [ 'text', 'url' ],
20
+ props_schema: {
21
+ key: { kind: 'plain', key: 'string', default: '' },
22
+ before: { kind: 'plain', key: 'string', default: 'prefix' },
23
+ fallback: { kind: 'plain', key: 'string', default: '' },
24
+ },
25
+ },
26
+ 'site-title': {
27
+ name: 'site-title',
28
+ label: 'Site Title',
29
+ group: 'site',
30
+ categories: [ 'text' ],
31
+ props_schema: {},
32
+ },
33
+ },
34
+ },
35
+ } as never );
36
+
37
+ describe( 'resolve-dynamic-tag', () => {
38
+ beforeEach( () => {
39
+ jest.clearAllMocks();
40
+ givenTags();
41
+ } );
42
+
43
+ describe( 'getDynamicTagNamesByCategories', () => {
44
+ it( 'returns only the tags whose categories intersect the prop categories', () => {
45
+ // Act
46
+ const names = getDynamicTagNamesByCategories( [ 'url' ] );
47
+
48
+ // Assert
49
+ expect( names ).toEqual( [ 'post-custom-field' ] );
50
+ } );
51
+
52
+ it( 'returns an empty list when no categories are requested', () => {
53
+ // Act & Assert
54
+ expect( getDynamicTagNamesByCategories( [] ) ).toEqual( [] );
55
+ } );
56
+ } );
57
+
58
+ describe( 'dynamicTagLLMResolver', () => {
59
+ it( 'fills the group from the registry and wraps provided scalar settings', () => {
60
+ // Act
61
+ const resolved = dynamicTagLLMResolver( {
62
+ name: 'post-custom-field',
63
+ settings: { key: 'price' },
64
+ } );
65
+
66
+ // Assert
67
+ expect( resolved ).toEqual( {
68
+ $$type: 'dynamic',
69
+ value: {
70
+ name: 'post-custom-field',
71
+ group: 'post',
72
+ settings: {
73
+ key: { $$type: 'string', value: 'price' },
74
+ before: { $$type: 'string', value: 'prefix' },
75
+ },
76
+ },
77
+ } );
78
+ } );
79
+
80
+ it( 'keeps already-wrapped settings values untouched', () => {
81
+ // Act
82
+ const resolved = dynamicTagLLMResolver( {
83
+ name: 'post-custom-field',
84
+ settings: { key: { $$type: 'string', value: 'price' } },
85
+ } ) as { value: { settings: Record< string, unknown > } };
86
+
87
+ // Assert
88
+ expect( resolved.value.settings.key ).toEqual( { $$type: 'string', value: 'price' } );
89
+ } );
90
+
91
+ it( 'returns a structurally complete value for unknown tags', () => {
92
+ // Act
93
+ const resolved = dynamicTagLLMResolver( { name: 'does-not-exist' } );
94
+
95
+ // Assert
96
+ expect( resolved ).toEqual( {
97
+ $$type: 'dynamic',
98
+ value: { name: 'does-not-exist', group: '', settings: {} },
99
+ } );
100
+ } );
101
+ } );
102
+ } );
@@ -12,6 +12,7 @@ import { type Utils as IUtils } from '@elementor/editor-variables';
12
12
  import { type z } from '@elementor/schema';
13
13
 
14
14
  import { mergeCustomCssText, readStoredCustomCssText } from './merge-custom-css';
15
+ import { DYNAMIC_PROP_TYPE_KEY, dynamicTagLLMResolver } from './resolve-dynamic-tag';
15
16
 
16
17
  // TODO: see https://elementor.atlassian.net/browse/ED-22513 for better cross-module access
17
18
  type XElementor = z.infer< z.ZodAny >;
@@ -34,7 +35,10 @@ export function resolvePropValue( value: unknown, forceKey?: string ): PropValue
34
35
  .Utils as typeof IUtils;
35
36
  return Schema.adjustLlmPropValueSchema( value as PropValue, {
36
37
  forceKey,
37
- transformers: Utils.globalVariablesLLMResolvers,
38
+ transformers: {
39
+ ...Utils.globalVariablesLLMResolvers,
40
+ [ DYNAMIC_PROP_TYPE_KEY ]: dynamicTagLLMResolver,
41
+ },
38
42
  } );
39
43
  }
40
44
 
@@ -0,0 +1,102 @@
1
+ import { type PropType, type PropValue } from '@elementor/editor-props';
2
+ import { getElementorConfig } from '@elementor/editor-v1-adapters';
3
+
4
+ export const DYNAMIC_PROP_TYPE_KEY = 'dynamic';
5
+
6
+ // `fallback` is a generic render-time default added to every tag; it is noise for configuration.
7
+ export const OMITTED_DYNAMIC_SETTING_KEYS = [ 'fallback' ] as const;
8
+
9
+ type SettingPropType = PropType & { key?: string };
10
+
11
+ export type AtomicDynamicTag = {
12
+ name: string;
13
+ label: string;
14
+ group: string;
15
+ categories: string[];
16
+ props_schema: Record< string, SettingPropType >;
17
+ };
18
+
19
+ type LlmDynamicValue = {
20
+ name?: string;
21
+ settings?: Record< string, unknown >;
22
+ };
23
+
24
+ export const getAtomicDynamicTags = (): Record< string, AtomicDynamicTag > => {
25
+ const config = getElementorConfig() as { atomicDynamicTags?: { tags?: Record< string, AtomicDynamicTag > } };
26
+ return config.atomicDynamicTags?.tags ?? {};
27
+ };
28
+
29
+ export const getDynamicTagNamesByCategories = ( categories: string[] ): string[] => {
30
+ if ( ! categories.length ) {
31
+ return [];
32
+ }
33
+ const wanted = new Set( categories );
34
+ return Object.values( getAtomicDynamicTags() )
35
+ .filter( ( tag ) => tag.categories?.some( ( category ) => wanted.has( category ) ) )
36
+ .map( ( tag ) => tag.name );
37
+ };
38
+
39
+ // Reconstructs an intact dynamic PropValue from whatever the LLM produced: the authoritative `group`
40
+ // is taken from the registry (never trusted from the model) and `settings` are rebuilt strictly from
41
+ // the tag's props schema (provided values are wrapped, omitted ones fall back to their defaults).
42
+ export const dynamicTagLLMResolver = ( value: unknown ): PropValue => {
43
+ const input = ( value ?? {} ) as LlmDynamicValue;
44
+ const tag = input.name ? getAtomicDynamicTags()[ input.name ] : undefined;
45
+
46
+ if ( ! tag ) {
47
+ return {
48
+ $$type: DYNAMIC_PROP_TYPE_KEY,
49
+ value: { name: input.name ?? '', group: '', settings: {} },
50
+ } as PropValue;
51
+ }
52
+
53
+ return {
54
+ $$type: DYNAMIC_PROP_TYPE_KEY,
55
+ value: {
56
+ name: tag.name,
57
+ group: tag.group,
58
+ settings: buildStrictSettings( tag.props_schema ?? {}, input.settings ?? {} ),
59
+ },
60
+ } as PropValue;
61
+ };
62
+
63
+ const buildStrictSettings = (
64
+ schema: Record< string, SettingPropType >,
65
+ provided: Record< string, unknown >
66
+ ): Record< string, unknown > => {
67
+ const settings: Record< string, unknown > = {};
68
+
69
+ for ( const [ key, propType ] of Object.entries( schema ) ) {
70
+ if ( ( OMITTED_DYNAMIC_SETTING_KEYS as readonly string[] ).includes( key ) ) {
71
+ continue;
72
+ }
73
+
74
+ const resolved =
75
+ provided[ key ] !== undefined
76
+ ? wrapSettingValue( provided[ key ], propType )
77
+ : defaultSettingValue( propType );
78
+
79
+ if ( resolved !== undefined && resolved !== null ) {
80
+ settings[ key ] = resolved;
81
+ }
82
+ }
83
+
84
+ return settings;
85
+ };
86
+
87
+ const wrapSettingValue = ( raw: unknown, propType: SettingPropType ): unknown => {
88
+ if ( raw !== null && typeof raw === 'object' ) {
89
+ return raw;
90
+ }
91
+ return propType.key ? { $$type: propType.key, value: raw } : raw;
92
+ };
93
+
94
+ const defaultSettingValue = ( propType: SettingPropType ): unknown => {
95
+ if ( propType.initial_value !== null && propType.initial_value !== undefined ) {
96
+ return propType.initial_value;
97
+ }
98
+ if ( propType.default !== null && propType.default !== undefined ) {
99
+ return wrapSettingValue( propType.default, propType );
100
+ }
101
+ return undefined;
102
+ };