@elementor/editor-canvas 4.2.0-beta2 → 4.3.0-1001

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.
Files changed (33) hide show
  1. package/dist/index.d.mts +1 -1
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +449 -1319
  4. package/dist/index.mjs +314 -1197
  5. package/package.json +22 -22
  6. package/src/index.ts +1 -1
  7. package/src/legacy/__tests__/init-legacy-views.test.ts +1 -1
  8. package/src/mcp/canvas-mcp.ts +4 -2
  9. package/src/mcp/resources/__tests__/available-widgets-resource.test.ts +85 -0
  10. package/src/mcp/resources/__tests__/dynamic-tags-resource.test.ts +27 -37
  11. package/src/mcp/resources/__tests__/widgets-schema-resource.test.ts +82 -74
  12. package/src/mcp/resources/available-widgets-resource.ts +35 -41
  13. package/src/mcp/resources/best-practices-resource.ts +34 -0
  14. package/src/mcp/resources/dynamic-tags-resource.ts +28 -29
  15. package/src/mcp/resources/widgets-schema-resource.ts +37 -177
  16. package/src/mcp/tools/build-composition/tool.ts +107 -218
  17. package/src/sync/element-added-event.ts +8 -0
  18. package/src/utils/__tests__/grid-outline-utils.test.ts +39 -0
  19. package/src/utils/grid-outline-utils.ts +13 -2
  20. package/src/composition-builder/__tests__/composition-builder.test.ts +0 -352
  21. package/src/composition-builder/composition-builder.ts +0 -350
  22. package/src/composition-builder/utils/__tests__/required-children-enforcer.test.ts +0 -79
  23. package/src/composition-builder/utils/__tests__/required-default-child-tags.test.ts +0 -35
  24. package/src/composition-builder/utils/required-children-enforcer.ts +0 -56
  25. package/src/composition-builder/utils/required-default-child-tags.ts +0 -22
  26. package/src/mcp/resources/best-practices.ts +0 -159
  27. package/src/mcp/resources/build-llm-guidance.ts +0 -110
  28. package/src/mcp/tools/build-composition/__tests__/xml-leaf-wrapper.test.ts +0 -117
  29. package/src/mcp/tools/build-composition/prompt.ts +0 -167
  30. package/src/mcp/tools/build-composition/schema.ts +0 -42
  31. package/src/mcp/tools/build-composition/xml-leaf-wrapper.ts +0 -68
  32. package/src/mcp/utils/__tests__/get-composition-target-container.test.ts +0 -59
  33. package/src/mcp/utils/get-composition-target-container.ts +0 -15
package/dist/index.mjs CHANGED
@@ -2,205 +2,37 @@
2
2
  import { v1ReadyEvent } from "@elementor/editor-v1-adapters";
3
3
 
4
4
  // src/mcp/resources/widgets-schema-resource.ts
5
- import { getWidgetsCache as getWidgetsCache2 } from "@elementor/editor-elements";
6
5
  import { ResourceTemplate } from "@elementor/editor-mcp";
7
- import {
8
- Schema
9
- } from "@elementor/editor-props";
10
-
11
- // src/mcp/utils/element-data-util.ts
12
- import { getWidgetsCache } from "@elementor/editor-elements";
13
- function hasV3Controls(controls) {
14
- return typeof controls === "object" && controls !== null && Object.keys(controls).length > 0;
15
- }
16
- function isWidgetAvailableForLLM(config) {
17
- if (!config) {
18
- return false;
19
- }
20
- if (config.meta?.llm_support === false) {
21
- return false;
22
- }
23
- if (config.title === "Component") {
24
- return false;
25
- }
26
- if (config.atomic_props_schema) {
27
- return true;
28
- }
29
- return hasV3Controls(config.controls);
30
- }
31
- function getWidgetVersion(config) {
32
- return config?.atomic_props_schema ? "v4" : "v3";
33
- }
34
- function getAvailableWidgets() {
35
- const cache = getWidgetsCache() ?? {};
36
- return Object.keys(cache).filter((widgetType) => isWidgetAvailableForLLM(cache[widgetType])).sort().map((widgetType) => {
37
- const config = cache[widgetType];
38
- const description = typeof config?.meta?.description === "string" ? config.meta.description : void 0;
39
- return {
40
- type: widgetType,
41
- version: getWidgetVersion(config),
42
- ...description && { description }
43
- };
44
- });
45
- }
46
-
47
- // src/composition-builder/utils/required-default-child-tags.ts
48
- function getRequiredDefaultChildTemplates(elementConfig) {
49
- const defaultChildren = elementConfig?.default_children;
50
- if (!Array.isArray(defaultChildren)) {
51
- return [];
52
- }
53
- return defaultChildren.filter((child) => child?.meta?.required ?? false);
54
- }
55
- function getRequiredDefaultChildTypes(elementConfig) {
56
- return getRequiredDefaultChildTemplates(elementConfig).map((child) => child.widgetType ?? child.elType ?? "").filter((type) => Boolean(type));
57
- }
58
-
59
- // src/mcp/resources/build-llm-guidance.ts
60
- var DEFAULT_STYLES_INSTRUCTION = "These are the default styles applied to the widget. Override only when necessary.";
61
- var DEFAULT_SETTINGS_INSTRUCTION = "These are the default settings applied to the widget. Omit them from elementConfig unless the user explicitly asks to change them.";
62
- var BASE_SETTING_PROP_HINT = "Has a widget default \u2014 omit unless user explicitly requests a change. See llm_guidance.default_settings.";
63
- function mergeInstructions(existing, additional) {
64
- if (typeof existing === "string" && existing.length > 0) {
65
- return `${existing} ${additional}`;
66
- }
67
- return additional;
68
- }
69
- function enrichPropertiesWithBaseSettingsHints(properties, baseSettingsKeys) {
70
- if (!baseSettingsKeys.length) {
71
- return properties;
72
- }
73
- const enriched = { ...properties };
74
- for (const key of baseSettingsKeys) {
75
- const propSchema = enriched[key];
76
- if (!propSchema) {
77
- continue;
78
- }
79
- enriched[key] = {
80
- ...propSchema,
81
- description: propSchema.description ? `${propSchema.description} ${BASE_SETTING_PROP_HINT}` : BASE_SETTING_PROP_HINT
82
- };
83
- }
84
- return enriched;
85
- }
86
- function buildLlmGuidance(widgetData, widgetType, allWidgets) {
87
- const defaultStyles = {};
88
- const baseStyleSchema = widgetData?.base_styles;
89
- if (baseStyleSchema) {
90
- Object.values(baseStyleSchema).forEach((stylePropType) => {
91
- stylePropType.variants.forEach((variant) => {
92
- Object.assign(defaultStyles, variant.props);
93
- });
94
- });
95
- }
96
- const baseSettings = widgetData?.base_settings ?? {};
97
- const hasDefaultStyles = Object.keys(defaultStyles).length > 0;
98
- const hasDefaultSettings = Object.keys(baseSettings).length > 0;
99
- const llmGuidance = {
100
- can_have_children: !!widgetData?.meta?.is_container
101
- };
102
- if (hasDefaultStyles) {
103
- llmGuidance.instructions = DEFAULT_STYLES_INSTRUCTION;
104
- llmGuidance.default_styles = defaultStyles;
105
- }
106
- if (hasDefaultSettings) {
107
- llmGuidance.instructions = mergeInstructions(llmGuidance.instructions, DEFAULT_SETTINGS_INSTRUCTION);
108
- llmGuidance.default_settings = baseSettings;
109
- }
110
- const allowedChildTypes = widgetData.allowed_child_types;
111
- const allowedParents = Object.entries(allWidgets).filter(([, parentConfig]) => parentConfig.allowed_child_types?.includes(widgetType)).map(([parentType]) => parentType);
112
- if (allowedChildTypes?.length || allowedParents.length) {
113
- llmGuidance.nesting = {
114
- ...allowedChildTypes?.length ? { allowed_child_types: allowedChildTypes } : {},
115
- ...allowedParents.length ? { allowed_parents: allowedParents } : {}
116
- };
117
- }
118
- const requiredDirectChildTags = getRequiredDefaultChildTypes(widgetData);
119
- if (requiredDirectChildTags.length) {
120
- llmGuidance.required_direct_children = requiredDirectChildTags;
121
- }
122
- return llmGuidance;
123
- }
124
-
125
- // src/mcp/resources/widgets-schema-resource.ts
126
- var V3_LAYOUT_CONTROL_TYPES = /* @__PURE__ */ new Set(["section", "tab", "tabs"]);
127
- function extractV3ControlsMetadata(controls) {
128
- if (!hasV3Controls(controls)) {
129
- return {};
130
- }
131
- const result = {};
132
- for (const [controlKey, raw] of Object.entries(controls)) {
133
- if (!raw || typeof raw !== "object") {
134
- continue;
135
- }
136
- const control = raw;
137
- const controlType = typeof control.type === "string" ? control.type : void 0;
138
- if (controlType && V3_LAYOUT_CONTROL_TYPES.has(controlType)) {
139
- continue;
140
- }
141
- const entry = {};
142
- if (Object.prototype.hasOwnProperty.call(control, "default")) {
143
- entry.default = control.default;
144
- }
145
- if (controlType) {
146
- entry.type = controlType;
147
- }
148
- if (Object.prototype.hasOwnProperty.call(control, "options") && control.options !== void 0) {
149
- const options = control.options;
150
- if (options && typeof options === "object" && !Array.isArray(options)) {
151
- entry.options = Object.keys(options);
152
- } else {
153
- entry.options = options;
154
- }
155
- }
156
- result[controlKey] = entry;
157
- }
158
- return result;
159
- }
6
+ import { httpService } from "@elementor/http-client";
160
7
  var CANVAS_SERVER_NAME = "editor-canvas";
161
8
  var WIDGET_SCHEMA_URI = "elementor://widgets/schema/{widgetType}";
162
9
  var WIDGET_SCHEMA_FULL_URI = `${CANVAS_SERVER_NAME}_${WIDGET_SCHEMA_URI}`;
163
- var BEST_PRACTICES_URI = "elementor://styles/best-practices";
10
+ var BEST_PRACTICES_URI = "elementor://style/best-practices";
164
11
  var BEST_PRACTICES_FULL_URI = `${CANVAS_SERVER_NAME}_${BEST_PRACTICES_URI}`;
12
+ var MCP_PROXY_URL = "elementor/v1/mcp-proxy";
13
+ var listWidgetTypes = async () => {
14
+ const { data } = await httpService().post(MCP_PROXY_URL, {
15
+ tool: "list-widget-schemas",
16
+ input: { summary: true }
17
+ });
18
+ return (data.data?.widgets ?? []).map((widget) => widget.type);
19
+ };
20
+ var fetchWidgetSchema = async (widgetType) => {
21
+ const { data } = await httpService().post(MCP_PROXY_URL, {
22
+ tool: "get-widget-schema",
23
+ input: { widget_type: widgetType }
24
+ });
25
+ return data.data ?? {};
26
+ };
165
27
  var initWidgetsSchemaResource = (reg) => {
166
28
  const { resource } = reg;
167
- resource(
168
- "styles-best-practices",
169
- BEST_PRACTICES_URI,
170
- {
171
- description: "Styling best practices"
172
- },
173
- async () => {
174
- return {
175
- contents: [
176
- {
177
- uri: BEST_PRACTICES_URI,
178
- text: `# Styling best practices
179
- Prefer using "em" and "rem" values for text-related sizes, padding and spacing. Use percentages for dynamic sizing relative to parent containers.
180
- This flexboxes are by default "flex" with "stretch" alignment. To ensure proper layout, define the "justify-content" and "align-items" as in the schema.
181
-
182
- Styling is provided as raw CSS. The css string must follow standard CSS syntax, with properties and values separated by semicolons, no selectors, or nesting rules allowed.
183
-
184
- ** CRITICAL - VARIABLES **
185
- When using global variables, ensure that the variables are defined in the ${"elementor://global-variables"} resource.
186
- Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
187
-
188
- `
189
- }
190
- ]
191
- };
192
- }
193
- );
194
29
  resource(
195
30
  "widget-schema-by-type",
196
31
  new ResourceTemplate(WIDGET_SCHEMA_URI, {
197
- list: () => {
198
- const cache = getWidgetsCache2() || {};
199
- const availableWidgets = Object.keys(cache).filter(
200
- (widgetType) => isWidgetAvailableForLLM(cache[widgetType])
201
- );
32
+ list: async () => {
33
+ const widgetTypes = await listWidgetTypes();
202
34
  return {
203
- resources: availableWidgets.map((widgetType) => ({
35
+ resources: widgetTypes.map((widgetType) => ({
204
36
  uri: `elementor://widgets/schema/${widgetType}`,
205
37
  name: "Widget schema for " + widgetType
206
38
  }))
@@ -212,52 +44,16 @@ Variables from the user context ARE NOT SUPPORTED AND WILL RESOLVE IN ERROR.
212
44
  },
213
45
  async (uri, variables) => {
214
46
  const widgetType = typeof variables.widgetType === "string" ? variables.widgetType : variables.widgetType?.[0];
215
- const widgetData = getWidgetsCache2()?.[widgetType];
216
- if (!widgetData) {
217
- throw new Error(`No prop schema found for element type: ${widgetType}`);
218
- }
219
- const propSchema = widgetData.atomic_props_schema;
220
- if (!propSchema) {
221
- if (!hasV3Controls(widgetData.controls)) {
222
- throw new Error(`No prop schema found for element type: ${widgetType}`);
223
- }
224
- const controlMetadata = extractV3ControlsMetadata(widgetData.controls);
225
- return {
226
- contents: [
227
- {
228
- uri: uri.toString(),
229
- mimeType: "application/json",
230
- text: JSON.stringify({
231
- widget_version: "v3",
232
- message: "This widget exists in the editor but has no atomic props schema (V4). Use control_metadata as non-authoritative hints from legacy controls.",
233
- fields_note: "All settings are optional; there is no JSON schema for this widget type.",
234
- properties: controlMetadata
235
- })
236
- }
237
- ]
238
- };
47
+ if (!widgetType) {
48
+ throw new Error("No widget type provided.");
239
49
  }
240
- const baseSettingsKeys = Object.keys(widgetData?.base_settings ?? {});
241
- const asJson = enrichPropertiesWithBaseSettingsHints(
242
- Object.fromEntries(
243
- Object.entries(propSchema).filter(([key, propType]) => Schema.isPropKeyConfigurable(key, propType)).map(([key, propType]) => [key, Schema.propTypeToJsonSchema(propType)])
244
- ),
245
- baseSettingsKeys
246
- );
247
- const description = typeof widgetData?.meta?.description === "string" ? widgetData.meta.description : void 0;
248
- const allWidgets = getWidgetsCache2() || {};
249
- const llmGuidance = buildLlmGuidance(widgetData, widgetType, allWidgets);
50
+ const schema2 = await fetchWidgetSchema(widgetType);
250
51
  return {
251
52
  contents: [
252
53
  {
253
54
  uri: uri.toString(),
254
55
  mimeType: "application/json",
255
- text: JSON.stringify({
256
- type: "object",
257
- properties: asJson,
258
- description,
259
- llm_guidance: llmGuidance
260
- })
56
+ text: JSON.stringify(schema2)
261
57
  }
262
58
  ]
263
59
  };
@@ -312,11 +108,11 @@ var initBreakpointsResource = (reg) => {
312
108
  };
313
109
 
314
110
  // src/mcp/utils/convert-css-to-atomic.ts
315
- import { httpService } from "@elementor/http-client";
111
+ import { httpService as httpService2 } from "@elementor/http-client";
316
112
  var CSS_TO_ATOMIC_URL = "elementor/v1/css-to-atomic";
317
113
  var SINGLE_BLOCK_KEY = "default";
318
114
  var convertBlocks = async (blocks) => {
319
- const { data } = await httpService().post(
115
+ const { data } = await httpService2().post(
320
116
  CSS_TO_ATOMIC_URL,
321
117
  { blocks }
322
118
  );
@@ -460,8 +256,8 @@ function toGridTracks(computedStyle) {
460
256
  return {
461
257
  columns: parseTrackList(computedStyle.gridTemplateColumns),
462
258
  rows: parseTrackList(computedStyle.gridTemplateRows),
463
- columnGap: toPx(computedStyle.columnGap),
464
- rowGap: toPx(computedStyle.rowGap),
259
+ columnGap: resolveGapPx(computedStyle.columnGap, computedStyle.width),
260
+ rowGap: resolveGapPx(computedStyle.rowGap, computedStyle.height),
465
261
  padding: {
466
262
  top: toPx(computedStyle.paddingTop),
467
263
  right: toPx(computedStyle.paddingRight),
@@ -536,6 +332,14 @@ function toPx(value) {
536
332
  const parsed = parseFloat(value);
537
333
  return Number.isFinite(parsed) ? parsed : 0;
538
334
  }
335
+ function resolveGapPx(value, referenceSize) {
336
+ if (value.trim().endsWith("%")) {
337
+ const percent = parseFloat(value);
338
+ const reference = parseFloat(referenceSize);
339
+ return Number.isFinite(percent) && Number.isFinite(reference) ? percent / 100 * reference : 0;
340
+ }
341
+ return toPx(value);
342
+ }
539
343
 
540
344
  // src/hooks/use-grid-children.ts
541
345
  import { useEffect as useEffect3, useState as useState2 } from "react";
@@ -2002,46 +1806,6 @@ function clipboardRootsAreAtomicForms(elements) {
2002
1806
  }
2003
1807
  return elements.every((el) => getClipboardElementType(el) === FORM_ELEMENT_TYPE);
2004
1808
  }
2005
- function hasFormAncestor(node) {
2006
- return node.closest(FORM_ELEMENT_TYPE) !== null;
2007
- }
2008
- function collectFormAncestorErrors(xml) {
2009
- const errors = [];
2010
- for (const node of xml.querySelectorAll("*")) {
2011
- if (!FORM_FIELD_ELEMENT_TYPES.has(node.tagName.toLowerCase())) {
2012
- continue;
2013
- }
2014
- if (hasFormAncestor(node)) {
2015
- continue;
2016
- }
2017
- const id = node.getAttribute("configuration-id");
2018
- errors.push(
2019
- `<${node.tagName}${id ? ` configuration-id="${id}"` : ""}> must be nested inside <e-form> (any ancestor depth is allowed).`
2020
- );
2021
- }
2022
- return errors;
2023
- }
2024
- function collectSubmitButtonErrors(xml) {
2025
- const errors = [];
2026
- for (const form of xml.querySelectorAll("e-form")) {
2027
- const submitButtons = form.querySelectorAll("e-form-submit-button");
2028
- if (submitButtons.length === 0) {
2029
- errors.push(`<e-form> has no <e-form-submit-button>.`);
2030
- } else if (submitButtons.length > 1) {
2031
- errors.push(`<e-form> has ${submitButtons.length} submit buttons \u2014 only 1 is allowed.`);
2032
- }
2033
- }
2034
- return errors;
2035
- }
2036
- function collectEmptyMessageErrors(xml) {
2037
- const errors = [];
2038
- for (const node of xml.querySelectorAll("e-form-success-message, e-form-error-message")) {
2039
- if (node.children.length === 0) {
2040
- errors.push(`<${node.tagName}> must have at least one child element (e.g. <e-atomic-paragraph>).`);
2041
- }
2042
- }
2043
- return errors;
2044
- }
2045
1809
 
2046
1810
  // src/form-structure/enforce-form-ancestor-commands.ts
2047
1811
  var FORM_FIELDS_OUTSIDE_ALERT = {
@@ -2751,7 +2515,7 @@ function initStyleTransformers() {
2751
2515
  }
2752
2516
 
2753
2517
  // src/legacy/init-legacy-views.ts
2754
- import { getWidgetsCache as getWidgetsCache3 } from "@elementor/editor-elements";
2518
+ import { getWidgetsCache } from "@elementor/editor-elements";
2755
2519
  import { __privateIsReady as isV1Ready, __privateListenTo, v1ReadyEvent as v1ReadyEvent2 } from "@elementor/editor-v1-adapters";
2756
2520
 
2757
2521
  // src/renderers/create-dom-renderer.ts
@@ -4067,7 +3831,7 @@ function registerElementType(type, elementTypeGenerator) {
4067
3831
  }
4068
3832
  function initLegacyViews() {
4069
3833
  __privateListenTo(v1ReadyEvent2(), () => {
4070
- const widgetsCache = getWidgetsCache3() ?? {};
3834
+ const widgetsCache = getWidgetsCache() ?? {};
4071
3835
  const renderer = createDomRenderer();
4072
3836
  registerProPromotionTypes(widgetsCache);
4073
3837
  Object.keys(widgetsCache).forEach((type) => {
@@ -4076,7 +3840,7 @@ function initLegacyViews() {
4076
3840
  });
4077
3841
  }
4078
3842
  function registerElementInLegacyManager(type, renderer) {
4079
- const element = (getWidgetsCache3() ?? {})[type];
3843
+ const element = (getWidgetsCache() ?? {})[type];
4080
3844
  if (!element?.atomic) {
4081
3845
  return;
4082
3846
  }
@@ -4158,68 +3922,85 @@ function initTabsModelExtensions() {
4158
3922
  }
4159
3923
 
4160
3924
  // src/mcp/canvas-mcp.ts
4161
- import { Schema as Schema6 } from "@elementor/editor-props";
3925
+ import { Schema as Schema4 } from "@elementor/editor-props";
4162
3926
 
4163
3927
  // src/mcp/resources/available-widgets-resource.ts
4164
- import { v1ReadyEvent as v1ReadyEvent3 } from "@elementor/editor-v1-adapters";
3928
+ import { httpService as httpService3 } from "@elementor/http-client";
3929
+ var MCP_PROXY_URL2 = "elementor/v1/mcp-proxy";
4165
3930
  var AVAILABLE_WIDGETS_URI = "elementor://context/available-widgets";
4166
3931
  var AVAILABLE_WIDGETS_URI_V4 = "elementor://context/available-widgets/v4";
4167
- var initAvailableWidgetsResource = (reg) => {
4168
- const { resource, sendResourceUpdated } = reg;
4169
- const buildContents = (uri, filterFunction = () => true) => {
4170
- const widgets = getAvailableWidgets().filter(filterFunction);
4171
- return {
4172
- contents: [
4173
- {
4174
- uri,
4175
- mimeType: "application/json",
4176
- text: JSON.stringify(widgets, null, 2)
4177
- }
4178
- ]
4179
- };
4180
- };
4181
- const notifyResourcesUpdated = () => {
4182
- sendResourceUpdated({
4183
- uri: AVAILABLE_WIDGETS_URI,
4184
- ...buildContents(AVAILABLE_WIDGETS_URI)
4185
- });
4186
- sendResourceUpdated({
4187
- uri: AVAILABLE_WIDGETS_URI_V4,
4188
- ...buildContents(AVAILABLE_WIDGETS_URI_V4, (w) => w.version === "v4")
4189
- });
3932
+ var fetchWidgets = async () => {
3933
+ const { data } = await httpService3().post(MCP_PROXY_URL2, {
3934
+ tool: "list-widget-schemas",
3935
+ input: { summary: true }
3936
+ });
3937
+ return data.data?.widgets ?? [];
3938
+ };
3939
+ var buildContents = async (uri) => {
3940
+ const widgets = await fetchWidgets();
3941
+ return {
3942
+ contents: [
3943
+ {
3944
+ uri,
3945
+ mimeType: "application/json",
3946
+ text: JSON.stringify(widgets, null, 2)
3947
+ }
3948
+ ]
4190
3949
  };
3950
+ };
3951
+ var initAvailableWidgetsResource = (reg) => {
3952
+ const { resource } = reg;
4191
3953
  resource(
4192
3954
  "available-widgets-v4",
4193
3955
  AVAILABLE_WIDGETS_URI_V4,
4194
3956
  {
4195
3957
  description: "All registered v4 version widgets"
4196
3958
  },
4197
- async () => buildContents(AVAILABLE_WIDGETS_URI_V4, (w) => w.version === "v4")
3959
+ async () => buildContents(AVAILABLE_WIDGETS_URI_V4)
4198
3960
  );
4199
3961
  resource(
4200
3962
  "available-widgets",
4201
3963
  AVAILABLE_WIDGETS_URI,
4202
3964
  {
4203
- description: "All registered widget types with v3/v4 version metadata and description."
3965
+ description: "All registered v4 widget types with description."
4204
3966
  },
4205
3967
  async () => buildContents(AVAILABLE_WIDGETS_URI)
4206
3968
  );
4207
- const eventName = v1ReadyEvent3().name;
4208
- const onV1Ready = () => {
4209
- const widgets = getAvailableWidgets();
4210
- if (widgets.length === 0) {
4211
- return;
3969
+ };
3970
+
3971
+ // src/mcp/resources/best-practices-resource.ts
3972
+ import { httpService as httpService4 } from "@elementor/http-client";
3973
+ var MCP_PROXY_URL3 = "elementor/v1/mcp-proxy";
3974
+ var BEST_PRACTICES_URI2 = "elementor://style/best-practices";
3975
+ var initBestPracticesResource = (reg) => {
3976
+ const { resource } = reg;
3977
+ resource(
3978
+ "style-best-practices",
3979
+ BEST_PRACTICES_URI2,
3980
+ {
3981
+ description: "Design quality guidelines for avoiding generic AI output: typography, color strategy, spacing, motion, and visual hierarchy best practices.",
3982
+ mimeType: "text/markdown"
3983
+ },
3984
+ async (uri) => {
3985
+ const { data } = await httpService4().get(MCP_PROXY_URL3, {
3986
+ params: { uri: uri.href }
3987
+ });
3988
+ return {
3989
+ contents: [
3990
+ {
3991
+ uri: uri.href,
3992
+ mimeType: "text/markdown",
3993
+ text: data.data
3994
+ }
3995
+ ]
3996
+ };
4212
3997
  }
4213
- window.removeEventListener(eventName, onV1Ready);
4214
- notifyResourcesUpdated();
4215
- };
4216
- window.addEventListener(eventName, onV1Ready);
4217
- onV1Ready();
3998
+ );
4218
3999
  };
4219
4000
 
4220
4001
  // src/mcp/resources/document-structure-resource.ts
4221
4002
  import {
4222
- getWidgetsCache as getWidgetsCache4
4003
+ getWidgetsCache as getWidgetsCache2
4223
4004
  } from "@elementor/editor-elements";
4224
4005
  import { __privateListenTo as listenTo, commandEndEvent as commandEndEvent4 } from "@elementor/editor-v1-adapters";
4225
4006
  var DOCUMENT_STRUCTURE_URI = "elementor://document/structure";
@@ -4285,7 +4066,7 @@ function resolveElementVersion(element) {
4285
4066
  return "v4";
4286
4067
  }
4287
4068
  const widgetType = element.model?.attributes?.widgetType;
4288
- if (widgetType && getWidgetsCache4()?.[widgetType]?.atomic_props_schema) {
4069
+ if (widgetType && getWidgetsCache2()?.[widgetType]?.atomic_props_schema) {
4289
4070
  return "v4";
4290
4071
  }
4291
4072
  return "v3";
@@ -4312,84 +4093,15 @@ function extractElementData(element) {
4312
4093
  }
4313
4094
 
4314
4095
  // src/mcp/resources/dynamic-tags-resource.ts
4315
- import { Schema as Schema2 } from "@elementor/editor-props";
4316
-
4317
- // src/mcp/utils/resolve-dynamic-tag.ts
4318
- import { getElementorConfig } from "@elementor/editor-v1-adapters";
4319
- var DYNAMIC_PROP_TYPE_KEY = "dynamic";
4320
- var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
4321
- var getAtomicDynamicTags = () => {
4322
- const config = getElementorConfig();
4323
- return config.atomicDynamicTags?.tags ?? {};
4324
- };
4325
- var getDynamicTagNamesByCategories = (categories) => {
4326
- if (!categories.length) {
4327
- return [];
4328
- }
4329
- const wanted = new Set(categories);
4330
- return Object.values(getAtomicDynamicTags()).filter((tag) => tag.categories?.some((category) => wanted.has(category))).map((tag) => tag.name);
4331
- };
4332
- var dynamicTagLLMResolver = (value) => {
4333
- const input = value ?? {};
4334
- const tag = input.name ? getAtomicDynamicTags()[input.name] : void 0;
4335
- if (!tag) {
4336
- return {
4337
- $$type: DYNAMIC_PROP_TYPE_KEY,
4338
- value: { name: input.name ?? "", group: "", settings: {} }
4339
- };
4340
- }
4341
- return {
4342
- $$type: DYNAMIC_PROP_TYPE_KEY,
4343
- value: {
4344
- name: tag.name,
4345
- group: tag.group,
4346
- settings: buildStrictSettings(tag.props_schema ?? {}, input.settings ?? {})
4347
- }
4348
- };
4349
- };
4350
- var buildStrictSettings = (schema2, provided) => {
4351
- const settings = {};
4352
- for (const [key, propType] of Object.entries(schema2)) {
4353
- if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
4354
- continue;
4355
- }
4356
- const resolved = provided[key] !== void 0 ? wrapSettingValue(provided[key], propType) : defaultSettingValue(propType);
4357
- if (resolved !== void 0 && resolved !== null) {
4358
- settings[key] = resolved;
4359
- }
4360
- }
4361
- return settings;
4362
- };
4363
- var wrapSettingValue = (raw, propType) => {
4364
- if (raw !== null && typeof raw === "object") {
4365
- return raw;
4366
- }
4367
- return propType.key ? { $$type: propType.key, value: raw } : raw;
4368
- };
4369
- var defaultSettingValue = (propType) => {
4370
- if (propType.initial_value !== null && propType.initial_value !== void 0) {
4371
- return propType.initial_value;
4372
- }
4373
- if (propType.default !== null && propType.default !== void 0) {
4374
- return wrapSettingValue(propType.default, propType);
4375
- }
4376
- return void 0;
4377
- };
4378
-
4379
- // src/mcp/resources/dynamic-tags-resource.ts
4096
+ import { httpService as httpService5 } from "@elementor/http-client";
4380
4097
  var DYNAMIC_TAGS_URI = "elementor://dynamic-tags";
4381
- var settingsSchema = (propsSchema) => {
4382
- return Object.fromEntries(
4383
- Object.entries(propsSchema ?? {}).filter(([key]) => !OMITTED_DYNAMIC_SETTING_KEYS.includes(key)).map(([key, propType]) => [key, Schema2.propTypeToJsonSchema(propType)])
4384
- );
4385
- };
4386
- var buildDynamicTagsList = () => {
4387
- return Object.values(getAtomicDynamicTags()).map((tag) => ({
4388
- name: tag.name,
4389
- label: tag.label,
4390
- categories: tag.categories,
4391
- settings: settingsSchema(tag.props_schema)
4392
- }));
4098
+ var MCP_PROXY_URL4 = "elementor/v1/mcp-proxy";
4099
+ var fetchDynamicTags = async () => {
4100
+ const { data } = await httpService5().post(MCP_PROXY_URL4, {
4101
+ tool: "list-dynamic-tags",
4102
+ input: {}
4103
+ });
4104
+ return data.data ?? [];
4393
4105
  };
4394
4106
  var initDynamicTagsResource = (reg) => {
4395
4107
  const { resource } = reg;
@@ -4400,15 +4112,18 @@ var initDynamicTagsResource = (reg) => {
4400
4112
  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.`,
4401
4113
  mimeType: "application/json"
4402
4114
  },
4403
- async (uri) => ({
4404
- contents: [
4405
- {
4406
- uri: uri.href,
4407
- mimeType: "application/json",
4408
- text: JSON.stringify(buildDynamicTagsList())
4409
- }
4410
- ]
4411
- })
4115
+ async (uri) => {
4116
+ const tags = await fetchDynamicTags();
4117
+ return {
4118
+ contents: [
4119
+ {
4120
+ uri: uri.href,
4121
+ mimeType: "application/json",
4122
+ text: JSON.stringify(tags)
4123
+ }
4124
+ ]
4125
+ };
4126
+ }
4412
4127
  );
4413
4128
  };
4414
4129
 
@@ -4582,7 +4297,7 @@ var initGeneralContextResource = (reg) => {
4582
4297
  };
4583
4298
 
4584
4299
  // src/mcp/resources/selected-element-resource.ts
4585
- import { getContainer as getContainer3, getSelectedElements, getWidgetsCache as getWidgetsCache5 } from "@elementor/editor-elements";
4300
+ import { getContainer as getContainer3, getSelectedElements, getWidgetsCache as getWidgetsCache3 } from "@elementor/editor-elements";
4586
4301
  import {
4587
4302
  __privateListenTo as listenTo4,
4588
4303
  commandEndEvent as commandEndEvent7
@@ -4683,7 +4398,7 @@ function resolveElementVersion2(container, widgetType) {
4683
4398
  if (container.model?.config?.atomic) {
4684
4399
  return "v4";
4685
4400
  }
4686
- if (widgetType && getWidgetsCache5()?.[widgetType]?.atomic_props_schema) {
4401
+ if (widgetType && getWidgetsCache3()?.[widgetType]?.atomic_props_schema) {
4687
4402
  return "v4";
4688
4403
  }
4689
4404
  return "v3";
@@ -4693,7 +4408,7 @@ function getElementProperties(container, widgetType) {
4693
4408
  if (!settings || typeof settings !== "object") {
4694
4409
  return null;
4695
4410
  }
4696
- const widgetConfig = widgetType ? getWidgetsCache5()?.[widgetType] : null;
4411
+ const widgetConfig = widgetType ? getWidgetsCache3()?.[widgetType] : null;
4697
4412
  const controls = widgetConfig?.controls;
4698
4413
  const filtered = {};
4699
4414
  for (const [key, value] of Object.entries(settings)) {
@@ -4731,33 +4446,110 @@ function getElementDisplayName(container) {
4731
4446
  }
4732
4447
 
4733
4448
  // src/mcp/tools/build-composition/tool.ts
4734
- import { getCurrentDocument } from "@elementor/editor-documents";
4735
- import {
4736
- createElement as createElement13,
4737
- deleteElement as deleteElement2,
4738
- getContainer as getContainer5,
4739
- getWidgetsCache as getWidgetsCache9
4740
- } from "@elementor/editor-elements";
4741
- import { dispatchMcpStylesAppliedEvent } from "@elementor/editor-mcp";
4449
+ import { getCurrentDocument, reloadCurrentDocument } from "@elementor/editor-documents";
4450
+ import { getContainer as getContainer4, selectElement } from "@elementor/editor-elements";
4451
+ import { AxiosError, httpService as httpService6 } from "@elementor/http-client";
4452
+ import { z } from "@elementor/schema";
4453
+ var MCP_PROXY_URL5 = "elementor/v1/mcp-proxy";
4454
+ var initBuildCompositionTool = (reg) => {
4455
+ const { addTool } = reg;
4456
+ addTool({
4457
+ name: "build-composition",
4458
+ description: "Build a V4 element composition on the Elementor canvas via the server-side MCP ability. Pass the raw XML tags directly as xmlStructure \u2014 do NOT wrap the value in <![CDATA[ ... ]]>, code fences, or quotes. The document is saved as a draft. Reload the editor after calling this tool to see the result.",
4459
+ schema: {
4460
+ xmlStructure: z.string().describe(
4461
+ 'Valid XML structure with custom Elementor widget tags. Every element MUST have a unique configuration-id attribute (e.g. <e-heading configuration-id="hero-title"></e-heading>). No attributes, classes, IDs, or text nodes in XML. Pass raw XML \u2014 do not wrap in CDATA.'
4462
+ ),
4463
+ elementConfig: z.record(
4464
+ z.string().describe("configuration-id"),
4465
+ z.record(z.string().describe("property name"), z.any().describe("PropValue"))
4466
+ ).optional().describe("Map configuration-id \u2192 widget PropValues ($$type + value)."),
4467
+ style: z.record(
4468
+ z.string().describe("configuration-id"),
4469
+ z.record(z.string().describe("CSS property name"), z.string().describe("CSS value"))
4470
+ ).optional().describe(
4471
+ "Map configuration-id \u2192 raw CSS declarations (property \u2192 value strings; no selectors). Server converts to native styles; unconvertible declarations become the element custom CSS."
4472
+ ),
4473
+ parentId: z.string().optional().describe("ID of the parent container. Omit or pass 'document' to insert at document root."),
4474
+ dryRun: z.boolean().optional().describe("If true, validate and return the resolved tree without persisting.")
4475
+ },
4476
+ outputSchema: {
4477
+ rootElementIds: z.array(z.string()),
4478
+ previewUrl: z.string(),
4479
+ version: z.string(),
4480
+ resolvedXml: z.string(),
4481
+ llmInstructions: z.string(),
4482
+ warnings: z.array(z.string()).optional()
4483
+ },
4484
+ handler: async ({ xmlStructure, elementConfig, style, parentId, dryRun }) => {
4485
+ const document2 = getCurrentDocument();
4486
+ if (!document2?.id) {
4487
+ throw new Error("No active document found.");
4488
+ }
4489
+ try {
4490
+ const { data } = await httpService6().post(MCP_PROXY_URL5, {
4491
+ tool: "build-composition",
4492
+ input: {
4493
+ post_id: document2.id,
4494
+ xml_structure: xmlStructure,
4495
+ element_config: elementConfig ?? {},
4496
+ style: style ?? {},
4497
+ parent_id: parentId ?? "document",
4498
+ dry_run: dryRun ?? false
4499
+ }
4500
+ });
4501
+ if (!dryRun) {
4502
+ await reloadCurrentDocument();
4503
+ const [firstRootId] = data.data.root_element_ids;
4504
+ if (firstRootId) {
4505
+ selectElement(firstRootId);
4506
+ getContainer4(firstRootId)?.view?.el?.scrollIntoView({
4507
+ behavior: "smooth",
4508
+ block: "center"
4509
+ });
4510
+ }
4511
+ }
4512
+ return {
4513
+ rootElementIds: data.data.root_element_ids,
4514
+ previewUrl: data.data.preview_url,
4515
+ version: data.data.version,
4516
+ resolvedXml: data.data.resolved_xml,
4517
+ llmInstructions: data.data.llm_instructions,
4518
+ warnings: data.data.warnings
4519
+ };
4520
+ } catch (error) {
4521
+ throw new Error(getErrorMessage(error));
4522
+ }
4523
+ }
4524
+ });
4525
+ };
4526
+ function getErrorMessage(error) {
4527
+ if (error instanceof AxiosError) {
4528
+ const data = error.response?.data;
4529
+ if (data?.message) {
4530
+ return data.code ? `${data.code}: ${data.message}` : data.message;
4531
+ }
4532
+ }
4533
+ if (error instanceof Error) {
4534
+ return error.message;
4535
+ }
4536
+ return "build-composition failed with an unknown error.";
4537
+ }
4742
4538
 
4743
- // src/composition-builder/composition-builder.ts
4744
- import {
4745
- createElement as createElement12,
4746
- deleteElement,
4747
- generateElementId as generateElementId2,
4748
- getContainer as getContainer4,
4749
- getWidgetsCache as getWidgetsCache8
4750
- } from "@elementor/editor-elements";
4539
+ // src/mcp/tools/configure-element/tool.ts
4540
+ import { getContainer as getContainer5, getWidgetsCache as getWidgetsCache6 } from "@elementor/editor-elements";
4541
+ import { dispatchMcpStylesAppliedEvent } from "@elementor/editor-mcp";
4542
+ import { Schema as Schema2 } from "@elementor/editor-props";
4751
4543
 
4752
4544
  // src/mcp/utils/do-update-element-property.ts
4753
4545
  import {
4754
4546
  createElementStyle,
4755
4547
  getElementStyles,
4756
- getWidgetsCache as getWidgetsCache7,
4548
+ getWidgetsCache as getWidgetsCache5,
4757
4549
  updateElementSettings,
4758
4550
  updateElementStyle
4759
4551
  } from "@elementor/editor-elements";
4760
- import { getPropSchemaFromCache as getPropSchemaFromCache2, Schema as Schema3 } from "@elementor/editor-props";
4552
+ import { getPropSchemaFromCache as getPropSchemaFromCache2, Schema } from "@elementor/editor-props";
4761
4553
  import { getStylesSchema as getStylesSchema2, getVariantByMeta } from "@elementor/editor-styles";
4762
4554
  import { __privateRunCommandSync as runCommandSync2 } from "@elementor/editor-v1-adapters";
4763
4555
 
@@ -4776,7 +4568,7 @@ var readStoredCustomCssText = (raw) => {
4776
4568
  };
4777
4569
 
4778
4570
  // src/mcp/utils/resolve-canonical-prop-name.ts
4779
- import { getWidgetsCache as getWidgetsCache6 } from "@elementor/editor-elements";
4571
+ import { getWidgetsCache as getWidgetsCache4 } from "@elementor/editor-elements";
4780
4572
  function buildAliasToCanonicalMap(schema2) {
4781
4573
  const aliasToCanonical = {};
4782
4574
  for (const [canonical, propType] of Object.entries(schema2)) {
@@ -4793,14 +4585,14 @@ function buildAliasToCanonicalMap(schema2) {
4793
4585
  return aliasToCanonical;
4794
4586
  }
4795
4587
  function resolveCanonicalPropName(elementType, propertyName) {
4796
- const schema2 = getWidgetsCache6()?.[elementType]?.atomic_props_schema;
4588
+ const schema2 = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
4797
4589
  if (!schema2 || schema2[propertyName]) {
4798
4590
  return propertyName;
4799
4591
  }
4800
4592
  return buildAliasToCanonicalMap(schema2)[propertyName] ?? propertyName;
4801
4593
  }
4802
4594
  function resolveCanonicalPropKeys(elementType, props) {
4803
- const schema2 = getWidgetsCache6()?.[elementType]?.atomic_props_schema;
4595
+ const schema2 = getWidgetsCache4()?.[elementType]?.atomic_props_schema;
4804
4596
  if (!schema2) {
4805
4597
  return { ...props };
4806
4598
  }
@@ -4827,6 +4619,68 @@ function resolveCanonicalPropKeys(elementType, props) {
4827
4619
  return resolved;
4828
4620
  }
4829
4621
 
4622
+ // src/mcp/utils/resolve-dynamic-tag.ts
4623
+ import { getElementorConfig } from "@elementor/editor-v1-adapters";
4624
+ var DYNAMIC_PROP_TYPE_KEY = "dynamic";
4625
+ var OMITTED_DYNAMIC_SETTING_KEYS = ["fallback"];
4626
+ var getAtomicDynamicTags = () => {
4627
+ const config = getElementorConfig();
4628
+ return config.atomicDynamicTags?.tags ?? {};
4629
+ };
4630
+ var getDynamicTagNamesByCategories = (categories) => {
4631
+ if (!categories.length) {
4632
+ return [];
4633
+ }
4634
+ const wanted = new Set(categories);
4635
+ return Object.values(getAtomicDynamicTags()).filter((tag) => tag.categories?.some((category) => wanted.has(category))).map((tag) => tag.name);
4636
+ };
4637
+ var dynamicTagLLMResolver = (value) => {
4638
+ const input = value ?? {};
4639
+ const tag = input.name ? getAtomicDynamicTags()[input.name] : void 0;
4640
+ if (!tag) {
4641
+ return {
4642
+ $$type: DYNAMIC_PROP_TYPE_KEY,
4643
+ value: { name: input.name ?? "", group: "", settings: {} }
4644
+ };
4645
+ }
4646
+ return {
4647
+ $$type: DYNAMIC_PROP_TYPE_KEY,
4648
+ value: {
4649
+ name: tag.name,
4650
+ group: tag.group,
4651
+ settings: buildStrictSettings(tag.props_schema ?? {}, input.settings ?? {})
4652
+ }
4653
+ };
4654
+ };
4655
+ var buildStrictSettings = (schema2, provided) => {
4656
+ const settings = {};
4657
+ for (const [key, propType] of Object.entries(schema2)) {
4658
+ if (OMITTED_DYNAMIC_SETTING_KEYS.includes(key)) {
4659
+ continue;
4660
+ }
4661
+ const resolved = provided[key] !== void 0 ? wrapSettingValue(provided[key], propType) : defaultSettingValue(propType);
4662
+ if (resolved !== void 0 && resolved !== null) {
4663
+ settings[key] = resolved;
4664
+ }
4665
+ }
4666
+ return settings;
4667
+ };
4668
+ var wrapSettingValue = (raw, propType) => {
4669
+ if (raw !== null && typeof raw === "object") {
4670
+ return raw;
4671
+ }
4672
+ return propType.key ? { $$type: propType.key, value: raw } : raw;
4673
+ };
4674
+ var defaultSettingValue = (propType) => {
4675
+ if (propType.initial_value !== null && propType.initial_value !== void 0) {
4676
+ return propType.initial_value;
4677
+ }
4678
+ if (propType.default !== null && propType.default !== void 0) {
4679
+ return wrapSettingValue(propType.default, propType);
4680
+ }
4681
+ return void 0;
4682
+ };
4683
+
4830
4684
  // src/mcp/utils/do-update-element-property.ts
4831
4685
  var LOCAL_STYLE_META = {
4832
4686
  breakpoint: "desktop",
@@ -4834,7 +4688,7 @@ var LOCAL_STYLE_META = {
4834
4688
  };
4835
4689
  function resolvePropValue(value, forceKey) {
4836
4690
  const Utils = window.elementorV2.editorVariables.Utils;
4837
- return Schema3.adjustLlmPropValueSchema(value, {
4691
+ return Schema.adjustLlmPropValueSchema(value, {
4838
4692
  forceKey,
4839
4693
  transformers: {
4840
4694
  ...Utils.globalVariablesLLMResolvers,
@@ -4932,7 +4786,7 @@ var doUpdateElementProperty = (params) => {
4932
4786
  }
4933
4787
  return;
4934
4788
  }
4935
- const elementPropSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
4789
+ const elementPropSchema = getWidgetsCache5()?.[elementType]?.atomic_props_schema;
4936
4790
  if (!elementPropSchema) {
4937
4791
  throw new Error(`No prop schema found for element type: ${elementType}`);
4938
4792
  }
@@ -4946,7 +4800,7 @@ var doUpdateElementProperty = (params) => {
4946
4800
  }
4947
4801
  const propKey = elementPropSchema[propertyName].key;
4948
4802
  const value = resolvePropValue(propertyValue, propKey);
4949
- const { valid, jsonSchema } = Schema3.validatePropValue(elementPropSchema[propertyName], propertyValue);
4803
+ const { valid, jsonSchema } = Schema.validatePropValue(elementPropSchema[propertyName], propertyValue);
4950
4804
  if (!valid) {
4951
4805
  throw new Error(
4952
4806
  `Invalid PropValue for elementId: ${elementId}. PropKey: ${propKey}, PropValue: ${JSON.stringify(
@@ -4965,752 +4819,11 @@ Expected Schema: ${jsonSchema}`
4965
4819
  runCommandSync2("document/save/set-is-modified", { status: true }, { internal: true });
4966
4820
  };
4967
4821
 
4968
- // src/composition-builder/utils/required-children-enforcer.ts
4969
- var REQUIRED_CHILD_SCHEMA_HINT = "Use the widget schema resource; under llm_guidance.required_direct_children for V4 widgets.";
4970
- var RequiredChildrenEnforcer = class {
4971
- elementType;
4972
- requiredTemplates;
4973
- constructor(elementType, widgetsCache) {
4974
- this.elementType = elementType;
4975
- this.requiredTemplates = getRequiredDefaultChildTemplates(widgetsCache[elementType]);
4976
- }
4977
- enforce(xml) {
4978
- if (this.requiredTemplates.length === 0) {
4979
- return;
4980
- }
4981
- const errors = [];
4982
- for (const rootNode of Array.from(xml.children)) {
4983
- this.collectMissingRequiredErrors(rootNode, errors);
4984
- }
4985
- if (errors.length) {
4986
- throw new Error(`${errors.join("\n")}
4987
- ${REQUIRED_CHILD_SCHEMA_HINT}`);
4988
- }
4989
- }
4990
- collectMissingRequiredErrors(node, errors) {
4991
- if (node.tagName === this.elementType) {
4992
- const existingChildTags = new Set(Array.from(node.children).map((child) => child.tagName));
4993
- const missingTags = this.requiredTemplates.map((child) => child.widgetType ?? child.elType ?? "").filter((type) => type && !existingChildTags.has(type));
4994
- if (missingTags.length) {
4995
- const configurationId = node.getAttribute("configuration-id");
4996
- const location2 = configurationId ? `<${node.tagName} configuration-id="${configurationId}">` : `<${node.tagName}>`;
4997
- errors.push(
4998
- `${location2} Missing required direct child element tag(s): ${missingTags.join(", ")}.`
4999
- );
5000
- }
5001
- }
5002
- for (const childNode of Array.from(node.children)) {
5003
- this.collectMissingRequiredErrors(childNode, errors);
5004
- }
5005
- }
5006
- };
5007
-
5008
- // src/composition-builder/composition-builder.ts
5009
- var CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE = "createElement did not return an element container with a model.";
5010
- var CompositionBuilder = class _CompositionBuilder {
5011
- elementConfig = {};
5012
- elementStylesConfig = {};
5013
- elementCustomCSS = {};
5014
- rootContainers = [];
5015
- api = {
5016
- createElement: createElement12,
5017
- deleteElement,
5018
- getWidgetsCache: getWidgetsCache8,
5019
- generateElementId: generateElementId2,
5020
- getContainer: getContainer4,
5021
- doUpdateElementProperty
5022
- };
5023
- xml;
5024
- static fromXMLString(xmlString, api = {}) {
5025
- const parser = new DOMParser();
5026
- const xmlDoc = parser.parseFromString(xmlString, "application/xml");
5027
- const errorNode = xmlDoc.querySelector("parsererror");
5028
- if (errorNode) {
5029
- throw new Error("Failed to parse XML string: " + errorNode.textContent);
5030
- }
5031
- return new _CompositionBuilder({
5032
- xml: xmlDoc,
5033
- api
5034
- });
5035
- }
5036
- constructor(opts) {
5037
- const { api = {}, elementConfig = {}, stylesConfig = {}, customCSS = {}, xml } = opts;
5038
- this.xml = xml;
5039
- Object.assign(this.api, api);
5040
- this.setElementConfig(elementConfig);
5041
- this.setStylesConfig(stylesConfig);
5042
- this.setCustomCSS(customCSS);
5043
- }
5044
- setElementConfig(config) {
5045
- this.elementConfig = config;
5046
- }
5047
- setStylesConfig(config) {
5048
- this.elementStylesConfig = config;
5049
- }
5050
- setCustomCSS(config) {
5051
- this.elementCustomCSS = config;
5052
- }
5053
- getXML() {
5054
- return this.xml;
5055
- }
5056
- buildModelTree(node, widgetsCache) {
5057
- const elementTag = node.tagName;
5058
- const isWidget = widgetsCache[elementTag]?.elType === "widget";
5059
- const id = this.api.generateElementId();
5060
- const children = Array.from(node.children).map((child) => this.buildModelTree(child, widgetsCache));
5061
- node.setAttribute("id", id);
5062
- const base = {
5063
- id,
5064
- skipDefaultChildren: true,
5065
- elements: children,
5066
- editor_settings: {
5067
- title: node.getAttribute("configuration-id") ?? void 0
5068
- },
5069
- elType: "widget"
5070
- };
5071
- if (isWidget) {
5072
- return { ...base, elType: "widget", widgetType: elementTag };
5073
- }
5074
- return { ...base, elType: elementTag };
5075
- }
5076
- async awaitViewRender(element) {
5077
- const view = element.view;
5078
- if (view?._currentRenderPromise instanceof Promise) {
5079
- await view._currentRenderPromise;
5080
- } else {
5081
- await Promise.resolve();
5082
- }
5083
- }
5084
- validateChildTypes(node, widgetsCache) {
5085
- const errors = [];
5086
- const allowedChildTypes = widgetsCache[node.tagName]?.allowed_child_types;
5087
- if (allowedChildTypes?.length) {
5088
- for (const child of Array.from(node.children)) {
5089
- if (!allowedChildTypes.includes(child.tagName)) {
5090
- errors.push(
5091
- `"${child.tagName}" is not allowed as a child of "${node.tagName}". Allowed: ${allowedChildTypes.join(", ")}`
5092
- );
5093
- }
5094
- }
5095
- }
5096
- for (const child of Array.from(node.children)) {
5097
- errors.push(...this.validateChildTypes(child, widgetsCache));
5098
- }
5099
- return errors;
5100
- }
5101
- matchNodeByConfigId(configId) {
5102
- const node = this.xml.querySelector(`[configuration-id="${configId}"]`);
5103
- if (!node) {
5104
- throw new Error(`Configuration id "${configId}" does not have target node.`);
5105
- }
5106
- const id = node.getAttribute("id");
5107
- if (!id) {
5108
- throw new Error(`Node with configuration id "${configId}" does not have element id.`);
5109
- }
5110
- const element = this.api.getContainer(id);
5111
- if (!element) {
5112
- throw new Error(`Element with id "${id}" not found but should exist.`);
5113
- }
5114
- return {
5115
- element,
5116
- node
5117
- };
5118
- }
5119
- async applyProperties() {
5120
- const configErrors = [];
5121
- const styleErrors = [];
5122
- const allConfigIds = /* @__PURE__ */ new Set([
5123
- ...Object.keys(this.elementConfig),
5124
- ...Object.keys(this.elementStylesConfig),
5125
- ...Object.keys(this.elementCustomCSS)
5126
- ]);
5127
- for (const configId of allConfigIds) {
5128
- let element, node;
5129
- try {
5130
- ({ element, node } = this.matchNodeByConfigId(configId));
5131
- } catch (matchErr) {
5132
- const msg = matchErr.message;
5133
- if (this.elementConfig[configId]) {
5134
- configErrors.push(msg);
5135
- }
5136
- if (this.elementStylesConfig[configId] || this.elementCustomCSS[configId]) {
5137
- styleErrors.push(msg);
5138
- }
5139
- continue;
5140
- }
5141
- const config = this.elementConfig[configId];
5142
- if (config) {
5143
- for (const [propertyName, propertyValue] of Object.entries(config)) {
5144
- try {
5145
- this.api.doUpdateElementProperty({
5146
- elementId: element.id,
5147
- propertyName,
5148
- propertyValue,
5149
- elementType: node.tagName
5150
- });
5151
- } catch (error) {
5152
- configErrors.push(error.message);
5153
- }
5154
- }
5155
- }
5156
- const styleConfig = this.elementStylesConfig[configId];
5157
- const hasInvalidStyles = false;
5158
- if (styleConfig) {
5159
- const validStylesPropValues = {};
5160
- for (const [styleName, stylePropValue] of Object.entries(styleConfig)) {
5161
- if (styleName === "$intention") {
5162
- continue;
5163
- } else {
5164
- validStylesPropValues[styleName] = stylePropValue;
5165
- }
5166
- }
5167
- if (Object.keys(validStylesPropValues).length > 0) {
5168
- try {
5169
- this.api.doUpdateElementProperty({
5170
- elementId: element.id,
5171
- propertyName: "_styles",
5172
- propertyValue: validStylesPropValues,
5173
- elementType: node.tagName
5174
- });
5175
- } catch (error) {
5176
- styleErrors.push(String(error));
5177
- }
5178
- }
5179
- }
5180
- const intentionCss = typeof styleConfig?.$intention === "string" ? styleConfig.$intention.trim() : "";
5181
- const fallbackCss = hasInvalidStyles && intentionCss ? intentionCss : "";
5182
- const mergedCustomCss = mergeCustomCssText(this.elementCustomCSS[configId], fallbackCss);
5183
- if (mergedCustomCss) {
5184
- try {
5185
- this.api.doUpdateElementProperty({
5186
- elementId: element.id,
5187
- propertyName: "_styles",
5188
- propertyValue: { custom_css: mergedCustomCss },
5189
- elementType: node.tagName
5190
- });
5191
- } catch (cssErr) {
5192
- styleErrors.push(String(cssErr));
5193
- }
5194
- }
5195
- await this.awaitViewRender(element);
5196
- }
5197
- return { configErrors, styleErrors };
5198
- }
5199
- async build(rootContainer) {
5200
- const widgetsCache = this.api.getWidgetsCache() || {};
5201
- new Set(this.xml.querySelectorAll("*")).forEach((node) => {
5202
- if (!widgetsCache[node.tagName]) {
5203
- throw new Error(`Unknown widget type: ${node.tagName}`);
5204
- }
5205
- });
5206
- const typesWithRequiredChildren = Object.keys(widgetsCache).filter(
5207
- (elementType) => getRequiredDefaultChildTemplates(widgetsCache[elementType]).length > 0
5208
- );
5209
- typesWithRequiredChildren.forEach((elementType) => {
5210
- new RequiredChildrenEnforcer(elementType, widgetsCache).enforce(this.xml);
5211
- });
5212
- const childTypeErrors = [];
5213
- for (const rootChild of Array.from(this.xml.children)) {
5214
- childTypeErrors.push(...this.validateChildTypes(rootChild, widgetsCache));
5215
- }
5216
- if (childTypeErrors.length) {
5217
- throw new Error(`Invalid element structure:
5218
- ${childTypeErrors.join("\n")}`);
5219
- }
5220
- const formErrors = [
5221
- ...collectFormAncestorErrors(this.xml),
5222
- ...collectSubmitButtonErrors(this.xml),
5223
- ...collectEmptyMessageErrors(this.xml)
5224
- ];
5225
- const children = Array.from(this.xml.children);
5226
- for (const childNode of children) {
5227
- const modelTree = this.buildModelTree(childNode, widgetsCache);
5228
- try {
5229
- const newElement = this.api.createElement({
5230
- container: rootContainer,
5231
- model: modelTree,
5232
- options: { useHistory: false }
5233
- });
5234
- if (!newElement?.model) {
5235
- throw new Error(CREATE_ELEMENT_INVALID_CONTAINER_MESSAGE);
5236
- }
5237
- this.rootContainers.push(newElement);
5238
- await this.awaitViewRender(newElement);
5239
- } catch (e) {
5240
- const attempToRestoreInvalidContainer = this.api.getContainer(modelTree.id);
5241
- if (attempToRestoreInvalidContainer) {
5242
- this.api.deleteElement({ container: attempToRestoreInvalidContainer });
5243
- }
5244
- throw e;
5245
- }
5246
- }
5247
- const { configErrors, styleErrors } = await this.applyProperties();
5248
- if (typeof window !== "undefined") {
5249
- const targetWindow = window.top || window;
5250
- targetWindow.dispatchEvent(
5251
- new CustomEvent("elementor/composition/built", {
5252
- detail: { rootContainers: this.rootContainers.map((c) => c.id) }
5253
- })
5254
- );
5255
- }
5256
- return {
5257
- configErrors,
5258
- styleErrors,
5259
- formErrors,
5260
- rootContainers: [...this.rootContainers]
5261
- };
5262
- }
5263
- };
5264
-
5265
- // src/utils/tracking.ts
5266
- import { trackEvent } from "@elementor/events";
5267
- var trackCanvasEvent = (data) => {
5268
- trackEvent(data);
5269
- };
5270
-
5271
- // src/mcp/utils/get-composition-target-container.ts
5272
- import { COMPONENT_DOCUMENT_TYPE } from "@elementor/editor-documents";
5273
- function getCompositionTargetContainer(documentContainer, documentType) {
5274
- const firstChild = documentContainer.children?.[0];
5275
- if (documentType === COMPONENT_DOCUMENT_TYPE && firstChild) {
5276
- return firstChild;
5277
- }
5278
- return documentContainer;
5279
- }
5280
-
5281
- // src/mcp/tools/build-composition/prompt.ts
5282
- import { toolPrompts } from "@elementor/editor-mcp";
5283
- var BUILD_COMPOSITIONS_GUIDE_URI = "elementor://canvas/tools/build-compositions-guide";
5284
- var generatePrompt = () => {
5285
- const buildCompositionsToolPrompt = toolPrompts("build-compositions");
5286
- buildCompositionsToolPrompt.description(`
5287
- # RESOURCES (Read before use)
5288
- - [elementor://global-classes] - Check FIRST for reusable classes
5289
- - [elementor://global-variables] - ONLY use variables defined here
5290
- - [${AVAILABLE_WIDGETS_URI}/v4]
5291
-
5292
- # TOOL SUPPORT
5293
- This tool support v4 elements only
5294
-
5295
- # WORKFLOW
5296
- 1. Check/create global classes via "manage-global-classes" tool
5297
- 2. Build composition (THIS TOOL) - minimal inline styles
5298
- 3. Apply classes via "apply-global-class" tool
5299
-
5300
- # XML STRUCTURE
5301
- - Use widget tags: \`<e-button configuration-id="btn1"></e-button>\`
5302
- - Containers: "e-flexbox", "e-div-block", "e-tabs"
5303
- - Every element needs unique "configuration-id"
5304
- - No attributes, classes, IDs, or text nodes in XML
5305
-
5306
- ## NESTED ELEMENTS
5307
- Some elements have internal tree structures (nesting). When using these elements, you MUST build the FULL tree in XML.
5308
- - Check \`llm_guidance.nesting\` in widget schemas for structure requirements
5309
- - \`llm_guidance.required_direct_children\` lists element types that must appear as direct child tags in XML (from widget defaults)
5310
- - \`allowed_child_types\` lists which element types can be nested inside
5311
- - \`allowed_parents\` lists which element types this element can be placed inside
5312
-
5313
- # CONFIGURATION
5314
- - Map configuration-id \u2192 elementConfig (props) + style (raw CSS declarations)
5315
- - elementConfig PropValues require \`$$type\` matching schema
5316
- - style is raw CSS (property \u2192 value strings); the server converts it to native styles and stores any unconvertible declarations as the element custom CSS
5317
- - NO LINKS in configuration
5318
- - Retry on errors up to 10x
5319
- - Check \`llm_guidance.default_settings\` in widget schemas \u2014 omit only keys listed there from elementConfig unless the user explicitly asks to change them
5320
-
5321
- # DYNAMIC TAGS
5322
- - 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\`).
5323
- - 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.
5324
- - Provide at that node: \`{ "$$type": "dynamic", "value": { "name": "<allowed tag>", "settings": { ... } } }\`
5325
- - Example (image): \`{ "$$type": "image", "value": { "src": { "$$type": "dynamic", "value": { "name": "<image tag>", "settings": { ... } } } } }\`
5326
- - Do NOT send \`group\` (it is resolved automatically). Populate \`settings\` strictly per the tag's schema; use \`{}\` only when it has none.
5327
-
5328
- Note about configuration ids: These names are visible to the end-user, make sure they make sense, related and relevant.
5329
-
5330
- # DESIGN PHILOSOPHY: CONTEXT-DRIVEN CREATIVITY
5331
-
5332
- **Use the user's context aggressively.** Business type, brand personality, target audience, and purpose should drive every design decision. A law firm needs gravitas; a children's app needs playfulness. Don't default to generic.
5333
-
5334
- ## SIZING: DEFAULT IS NO SIZE (CRITICAL)
5335
-
5336
- **DO NOT specify height or width unless you have a specific visual reason.**
5337
-
5338
- Flexbox and CSS already handle sizing automatically:
5339
- - Containers grow to fit their content
5340
- - Flex children distribute space via flex properties, not width/height
5341
- - Text elements size to their content
5342
-
5343
- WHEN TO SPECIFY SIZE:
5344
- - min-height on ROOT section for viewport-spanning hero (use min-height, NOT height)
5345
- - max-width for contained content areas (e.g., max-width: 60rem)
5346
- - Explicit aspect ratios for media containers
5347
-
5348
- NEVER SPECIFY:
5349
- - height on nested containers (causes overflow)
5350
- - width on flex children (use flex-basis or gap instead)
5351
- - 100vh on anything except root-level sections
5352
- - Any size "just to be safe" - if unsure, OMIT IT
5353
-
5354
- vh units are VIEWPORT-relative. Nested 100vh inside 100vh = 200vh overflow.
5355
-
5356
- GOOD: \`<e-flexbox>content naturally sizes</e-flexbox>\`
5357
- BAD: \`<e-flexbox style="height:100vh"><e-div-block style="height:100vh">overflow</e-div-block></e-flexbox>\`
5358
-
5359
- ## Layout Variety (Break the Template)
5360
- - AVOID: Full-width 100vh hero \u2192 three columns \u2192 testimonials \u2192 CTA (every AI does this)
5361
- - VARY heights: Use auto-height sections with generous padding (6rem+). Let content breathe
5362
- - VARY widths: Not everything spans full width. Use contained sections (max-width: 960px) mixed with edge-to-edge
5363
- - ASYMMETRIC grids: 2:1, 1:3, offset layouts. Avoid equal column widths
5364
- - Negative space as design element: Large margins create focus and sophistication
5365
- - Break alignment intentionally: Offset headings, overlapping elements, broken grids
5366
-
5367
- ## Visual Depth & Effects
5368
- - Layer elements: Overlapping cards, text over images, floating elements
5369
- - Subtle shadows with color tint (not pure black): \`box-shadow: 0 20px 60px rgba(<brand-color-here>, 0.15)\`
5370
- - Gradient overlays on images for text readability
5371
- - Border radius variation: Mix sharp (0) and soft (1rem+) corners purposefully
5372
- - Backdrop blur for glassmorphism where appropriate
5373
- - Micro-interactions via CSS: hover transforms, transitions (0.3s ease)
5374
-
5375
- ## Typography with Character
5376
- - Display fonts for headlines (from user's brand or contextually appropriate)
5377
- - Size contrast: 4rem+ headlines vs 1rem body. Make hierarchy unmistakable
5378
- - Letter-spacing: Tight for large headlines (-0.02em), loose for small caps (0.1em)
5379
- - Line-height: Tight for headlines (1.1), generous for body (1.6-1.8)
5380
- - Text decoration: Underlines, highlights, gradient text for emphasis
5381
-
5382
- ## Color with Purpose
5383
- - Extract palette from user context (brand colors, industry norms, mood)
5384
- - 60-30-10 rule: dominant, secondary, accent
5385
- - Tinted neutrals over pure grays: warm (#faf8f5, #2d2a26) or cool (#f5f7fa, #1e2430)
5386
- - Color blocking: Large colored sections create visual rhythm
5387
- - Gradient directions: Diagonal (135deg, 225deg) feel more dynamic than vertical
5388
-
5389
- ## Spacing Strategy
5390
- - Section padding: 6rem-10rem vertical, creating breathing room
5391
- - Rhythm variation: Tight groups (2rem) with generous gaps between (6rem)
5392
- - Use rem/em exclusively for responsive scaling
5393
- - Generous padding on CTAs: min 1rem 2.5rem
5394
-
5395
- # HARD CONSTRAINTS
5396
- - Variables ONLY from [elementor://global-variables] (others throw errors)
5397
- - Avoid SVG widgets unless assets are pre-uploaded
5398
- - Check \`llm_guidance\` in widget schemas (\`default_styles\`, nesting, required children)
5399
-
5400
- # PARAMETERS
5401
- - **xmlStructure**: Valid XML with configuration-id attributes
5402
- - **elementConfig**: configuration-id \u2192 widget PropValues
5403
- - **style**: configuration-id \u2192 raw CSS declarations (property \u2192 value strings; no selectors)
5404
- `);
5405
- buildCompositionsToolPrompt.example(`
5406
- Section with heading + button (NO explicit heights - content sizes naturally):
5407
- {
5408
- xmlStructure: "<e-flexbox configuration-id="Main Section"><e-heading configuration-id="Section Title"></e-heading><e-button configuration-id="Call to Action"></e-button></e-flexbox>",
5409
- elementConfig: {
5410
- "section1": { "tag": { "$$type": "string", "value": "section" } }
5411
- },
5412
- style: {
5413
- "Section Title": {
5414
- "padding": "6rem 4rem",
5415
- "background": "linear-gradient(135deg, #faf8f5 0%, #f0ebe4 100%)",
5416
- "font-size": "3.5rem",
5417
- "color": "#2d2a26"
5418
- }
5419
- }
5420
- }
5421
- Note: No height/width specified on any element - flexbox handles layout automatically.
5422
- `);
5423
- buildCompositionsToolPrompt.parameter(
5424
- "xmlStructure",
5425
- `Valid XML structure with custom elementor tags and configuration-id attributes.`
5426
- );
5427
- buildCompositionsToolPrompt.parameter("elementConfig", `Record mapping configuration IDs to widget PropValues.`);
5428
- buildCompositionsToolPrompt.parameter(
5429
- "style",
5430
- `Record mapping configuration IDs to raw CSS declarations (property \u2192 value strings).`
5431
- );
5432
- buildCompositionsToolPrompt.instruction(
5433
- `Element IDs in the returned XML represent actual widgets. Use these IDs for subsequent styling or configuration changes.`
5434
- );
5435
- return buildCompositionsToolPrompt.prompt();
5436
- };
5437
-
5438
- // src/mcp/tools/build-composition/schema.ts
5439
- import { z } from "@elementor/schema";
5440
- var inputSchema = {
5441
- xmlStructure: z.string().describe("The XML structure representing the composition to be built"),
5442
- elementConfig: z.record(
5443
- z.string().describe("The configuration id"),
5444
- z.record(
5445
- z.string().describe("property name"),
5446
- z.any().describe(`The PropValue for the property, refer to ${WIDGET_SCHEMA_URI}`)
5447
- )
5448
- ).describe("A record mapping element IDs to their configuration objects. REQUIRED"),
5449
- style: z.record(
5450
- z.string().describe("The configuration id"),
5451
- z.record(
5452
- z.string().describe('A CSS property name, e.g. "color", "padding".'),
5453
- z.string().describe('A CSS value, e.g. "6rem 4rem", "#2d2a26".')
5454
- )
5455
- ).describe(
5456
- "A record mapping element configuration IDs to their raw CSS declarations (property\u2192value). Converted to native styles server-side; any declaration that cannot be converted is stored as the element custom CSS."
5457
- ).default({})
5458
- };
5459
- var outputSchema = {
5460
- errors: z.string().describe("Error message if the composition building failed").optional(),
5461
- xmlStructure: z.string().describe(
5462
- "The built XML structure as a string. Must use this XML after completion of building the composition, it contains real IDs."
5463
- ).optional(),
5464
- llm_instructions: z.string().describe("Instructions what to do next, Important to follow these instructions!").optional()
5465
- };
5466
-
5467
- // src/mcp/tools/build-composition/xml-leaf-wrapper.ts
5468
- var DIV_BLOCK_TAG = "e-div-block";
5469
- var ZERO_SPACING = {
5470
- $$type: "size",
5471
- value: {
5472
- size: {
5473
- $$type: "number",
5474
- value: 0
5475
- },
5476
- unit: {
5477
- $$type: "string",
5478
- value: "px"
5479
- }
5480
- }
5481
- };
5482
- function adaptLeafRootParams(params) {
5483
- const doc = new DOMParser().parseFromString(params.xmlStructure, "application/xml");
5484
- const rootElement = doc.documentElement;
5485
- if (!isLeafWidget(rootElement.tagName, params.widgetsCache)) {
5486
- return params;
5487
- }
5488
- const wrapperConfigId = getDivBlockWrapperConfigId(params.widgetsCache);
5489
- return {
5490
- ...params,
5491
- xmlStructure: serializeWrapped(doc, rootElement, wrapperConfigId),
5492
- stylesConfig: {
5493
- ...params.stylesConfig,
5494
- [wrapperConfigId]: {
5495
- margin: ZERO_SPACING,
5496
- padding: ZERO_SPACING,
5497
- ...params.stylesConfig[wrapperConfigId]
5498
- }
5499
- }
5500
- };
5501
- }
5502
- function getDivBlockWrapperConfigId(widgetsCache) {
5503
- return widgetsCache[DIV_BLOCK_TAG]?.title ?? DIV_BLOCK_TAG;
5504
- }
5505
- function isLeafWidget(tagName, widgetsCache) {
5506
- return widgetsCache[tagName]?.elType === "widget";
5507
- }
5508
- function serializeWrapped(doc, rootElement, wrapperConfigId) {
5509
- const wrapper = doc.createElement(DIV_BLOCK_TAG);
5510
- wrapper.setAttribute("configuration-id", wrapperConfigId);
5511
- wrapper.appendChild(rootElement.cloneNode(true));
5512
- const wrappedDoc = new DOMParser().parseFromString(`<${DIV_BLOCK_TAG} />`, "application/xml");
5513
- wrappedDoc.replaceChild(wrapper, wrappedDoc.documentElement);
5514
- return new XMLSerializer().serializeToString(wrappedDoc);
5515
- }
5516
-
5517
- // src/mcp/tools/build-composition/tool.ts
5518
- var ELEMENT_ADDED_EVENT = "elementor/canvas/element-added";
5519
- var initBuildCompositionsTool = (reg) => {
5520
- const { addTool, resource } = reg;
5521
- resource(
5522
- "build-compositions-guide",
5523
- BUILD_COMPOSITIONS_GUIDE_URI,
5524
- {
5525
- title: "Build Compositions Guide",
5526
- description: "Detailed guide for using the build-compositions tool",
5527
- mimeType: "text/plain"
5528
- },
5529
- async (uri) => ({
5530
- contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt() }]
5531
- })
5532
- );
5533
- addTool({
5534
- name: "build-compositions",
5535
- description: "Build V4 element compositions on the Elementor canvas. Read the guide resource before use.",
5536
- schema: inputSchema,
5537
- requiredResources: [
5538
- { description: "Build compositions guide", uri: BUILD_COMPOSITIONS_GUIDE_URI },
5539
- { description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
5540
- { description: "Global Classes", uri: "elementor://global-classes" },
5541
- { description: "Global Variables", uri: "elementor://global-variables" },
5542
- { description: "Styles best practices", uri: BEST_PRACTICES_URI },
5543
- { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 },
5544
- { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5545
- ],
5546
- outputSchema,
5547
- handler: async (rawParams) => {
5548
- assertCompositionXmlUsesV4WidgetsOnly(rawParams.xmlStructure);
5549
- const { stylesConfig: convertedStyles, customCSS } = await convertCompositionStyles(rawParams.style);
5550
- const { xmlStructure, elementConfig, stylesConfig } = adaptLeafRootParams({
5551
- ...rawParams,
5552
- stylesConfig: convertedStyles,
5553
- widgetsCache: getWidgetsCache9() ?? {}
5554
- });
5555
- let generatedXML = "";
5556
- const errors = [];
5557
- const rootContainers = [];
5558
- const documentContainer = getContainer5("document");
5559
- const currentDocument = getCurrentDocument();
5560
- const targetContainer = getCompositionTargetContainer(documentContainer, currentDocument?.type.value);
5561
- try {
5562
- const compositionBuilder = CompositionBuilder.fromXMLString(xmlStructure, {
5563
- createElement: createElement13,
5564
- deleteElement: deleteElement2,
5565
- getWidgetsCache: getWidgetsCache9
5566
- });
5567
- compositionBuilder.setElementConfig(elementConfig);
5568
- compositionBuilder.setStylesConfig(stylesConfig);
5569
- compositionBuilder.setCustomCSS(customCSS);
5570
- const {
5571
- configErrors,
5572
- formErrors,
5573
- rootContainers: generatedRootContainers
5574
- } = await compositionBuilder.build(targetContainer);
5575
- rootContainers.push(...generatedRootContainers);
5576
- generatedXML = new XMLSerializer().serializeToString(compositionBuilder.getXML());
5577
- rootContainers.forEach((container) => {
5578
- const elementData = container.model?.toJSON();
5579
- if (elementData) {
5580
- onElementAdded(elementData);
5581
- }
5582
- });
5583
- Object.values(stylesConfig).forEach((styleValue) => {
5584
- dispatchMcpStylesAppliedEvent({ styleValue });
5585
- });
5586
- if (configErrors.length) {
5587
- errors.push(...configErrors.map((msg) => new Error(msg)));
5588
- }
5589
- if (formErrors.length) {
5590
- errors.push(...formErrors.map((msg) => new Error(msg)));
5591
- }
5592
- } catch (error) {
5593
- errors.push(error);
5594
- }
5595
- if (errors.length) {
5596
- rootContainers.forEach((rootContainer) => {
5597
- deleteElement2({
5598
- container: rootContainer,
5599
- options: { useHistory: false }
5600
- });
5601
- });
5602
- const errorMessages = errors.map((e) => {
5603
- if (typeof e === "string") {
5604
- return e;
5605
- }
5606
- if (e instanceof Error) {
5607
- return e.message || String(e);
5608
- }
5609
- if (typeof e === "object" && e !== null) {
5610
- return JSON.stringify(e);
5611
- }
5612
- return String(e);
5613
- }).filter(
5614
- (msg) => msg && msg.trim() !== "" && msg !== "{}" && msg !== "null" && msg !== "undefined"
5615
- );
5616
- if (errorMessages.length === 0) {
5617
- throw new Error(
5618
- "Failed to build composition: Unknown error occurred. No error details available."
5619
- );
5620
- }
5621
- const errorText = `Failed to build composition with the following errors:
5622
-
5623
- ${errorMessages.join(
5624
- "\n\n"
5625
- )}`;
5626
- throw new Error(errorText);
5627
- }
5628
- return {
5629
- xmlStructure: generatedXML,
5630
- errors: errors?.length ? errors.map((e) => typeof e === "string" ? e : e.message).join("\n\n") : void 0,
5631
- llm_instructions: `The composition was built successfully with element IDs embedded in the XML.
5632
-
5633
- **CRITICAL NEXT STEPS** (Follow in order):
5634
- 1. **Apply Global Classes**: Use "apply-global-class" tool to apply the global classes you created BEFORE building this composition
5635
- - Check the created element IDs in the returned XML
5636
- - Apply semantic classes (heading-primary, button-cta, etc.) to appropriate elements
5637
-
5638
- 2. **Fine-tune if needed**: Use "configure-element" tool only for element-specific adjustments that don't warrant global classes
5639
-
5640
- Remember: Global classes ensure design consistency and reusability. Don't skip applying them!
5641
- `
5642
- };
5643
- }
5644
- });
5645
- };
5646
- async function convertCompositionStyles(style) {
5647
- const stylesConfig = {};
5648
- const customCSS = {};
5649
- if (!style || Object.keys(style).length === 0) {
5650
- return { stylesConfig, customCSS };
5651
- }
5652
- const results = await convertStyleBlocksToAtomic(style);
5653
- for (const [configId, { props, customCss }] of Object.entries(results)) {
5654
- stylesConfig[configId] = props;
5655
- if (customCss) {
5656
- customCSS[configId] = customCss;
5657
- }
5658
- }
5659
- return { stylesConfig, customCSS };
5660
- }
5661
- function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
5662
- const doc = new DOMParser().parseFromString(xmlStructure, "application/xml");
5663
- if (doc.querySelector("parsererror")) {
5664
- throw new Error("Failed to parse XML string: " + doc);
5665
- }
5666
- const widgetsCache = getWidgetsCache9() ?? {};
5667
- for (const node of doc.querySelectorAll("*")) {
5668
- const type = node.tagName;
5669
- const widgetData = widgetsCache[type];
5670
- if (!widgetData) {
5671
- continue;
5672
- }
5673
- if (widgetData.elType !== "widget") {
5674
- continue;
5675
- }
5676
- if (!isWidgetAvailableForLLM(widgetData) || !widgetData.atomic_props_schema) {
5677
- throw new Error(`This tool does not support element type: ${type}`);
5678
- }
5679
- }
5680
- }
5681
- function onElementAdded(element) {
5682
- const elType = element.elType ?? "";
5683
- const widgetType = element.widgetType ?? "";
5684
- const elementName = elType === "widget" ? widgetType : elType;
5685
- trackCanvasEvent({
5686
- eventName: "add_element",
5687
- executed_by: "mcp_tool",
5688
- element_name: elementName,
5689
- element_type: elType,
5690
- widget_type: widgetType
5691
- });
5692
- const event = {
5693
- element,
5694
- executedBy: "mcp_tool"
5695
- };
5696
- window.dispatchEvent(new CustomEvent(ELEMENT_ADDED_EVENT, { detail: event }));
5697
- if (element.elements?.length) {
5698
- element.elements?.forEach((childElement) => {
5699
- onElementAdded(childElement);
5700
- });
5701
- }
5702
- }
5703
-
5704
- // src/mcp/tools/configure-element/tool.ts
5705
- import { getContainer as getContainer6, getWidgetsCache as getWidgetsCache10 } from "@elementor/editor-elements";
5706
- import { dispatchMcpStylesAppliedEvent as dispatchMcpStylesAppliedEvent2 } from "@elementor/editor-mcp";
5707
- import { Schema as Schema4 } from "@elementor/editor-props";
5708
-
5709
4822
  // src/mcp/tools/configure-element/prompt.ts
5710
- import { toolPrompts as toolPrompts2 } from "@elementor/editor-mcp";
4823
+ import { toolPrompts } from "@elementor/editor-mcp";
5711
4824
  var CONFIGURE_ELEMENT_GUIDE_URI = "elementor://canvas/tools/configure-element-guide";
5712
- var generatePrompt2 = () => {
5713
- const configureElementToolPrompt = toolPrompts2("configure-element");
4825
+ var generatePrompt = () => {
4826
+ const configureElementToolPrompt = toolPrompts("configure-element");
5714
4827
  configureElementToolPrompt.description(`
5715
4828
  Configure an existing element on the page.
5716
4829
 
@@ -5818,11 +4931,11 @@ NO Advanced tab. Never mention Advanced tab.
5818
4931
  `);
5819
4932
  return configureElementToolPrompt.prompt();
5820
4933
  };
5821
- var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt2();
4934
+ var CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt();
5822
4935
 
5823
4936
  // src/mcp/tools/configure-element/schema.ts
5824
4937
  import { z as z2 } from "@elementor/schema";
5825
- var inputSchema2 = {
4938
+ var inputSchema = {
5826
4939
  propertiesToChange: z2.record(
5827
4940
  z2.string().describe("The property name."),
5828
4941
  z2.any().describe(`PropValue, refer to [${WIDGET_SCHEMA_URI}] by correct type, as appears in elementType`),
@@ -5839,7 +4952,7 @@ var inputSchema2 = {
5839
4952
  elementType: z2.string().describe("The type of the element to retrieve the schema"),
5840
4953
  elementId: z2.string().describe("The unique id of the element to configure")
5841
4954
  };
5842
- var outputSchema2 = {
4955
+ var outputSchema = {
5843
4956
  success: z2.boolean().describe(
5844
4957
  "Whether the configuration change was successful, only if propertyName and propertyValue are provided"
5845
4958
  )
@@ -5857,27 +4970,27 @@ var initConfigureElementTool = (reg) => {
5857
4970
  mimeType: "text/plain"
5858
4971
  },
5859
4972
  async (uri) => ({
5860
- contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt2() }]
4973
+ contents: [{ uri: uri.href, mimeType: "text/plain", text: generatePrompt() }]
5861
4974
  })
5862
4975
  );
5863
4976
  addTool({
5864
4977
  name: "configure-element",
5865
4978
  description: "Configure an existing V4 element's properties and styles. Read the guide resource before use.",
5866
- schema: inputSchema2,
5867
- outputSchema: outputSchema2,
4979
+ schema: inputSchema,
4980
+ outputSchema,
5868
4981
  requiredResources: [
5869
4982
  { description: "Widgets schema", uri: WIDGET_SCHEMA_URI },
5870
4983
  { description: "Configure element guide", uri: CONFIGURE_ELEMENT_GUIDE_URI },
5871
4984
  { description: "Dynamic tags catalog", uri: DYNAMIC_TAGS_URI }
5872
4985
  ],
5873
4986
  handler: async ({ elementId, propertiesToChange, elementType, style }) => {
5874
- const widgetData = getWidgetsCache10()?.[elementType];
4987
+ const widgetData = getWidgetsCache6()?.[elementType];
5875
4988
  if (!widgetData) {
5876
4989
  throw new Error(
5877
4990
  `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
5878
4991
  );
5879
4992
  }
5880
- const container = getContainer6(elementId);
4993
+ const container = getContainer5(elementId);
5881
4994
  if (!container) {
5882
4995
  throw new Error(`Element with id ${elementId} not found`);
5883
4996
  }
@@ -5893,7 +5006,7 @@ var initConfigureElementTool = (reg) => {
5893
5006
  const propertiesToUpdate = resolveCanonicalPropKeys(elementType, propertiesToChange);
5894
5007
  const toUpdate = Object.entries(propertiesToUpdate);
5895
5008
  for (const [propertyName, propertyValue] of toUpdate) {
5896
- if (!Schema4.isPropKeyConfigurable(propertyName)) {
5009
+ if (!Schema2.isPropKeyConfigurable(propertyName)) {
5897
5010
  throw new Error(`Not allowed to update ${propertyName}`);
5898
5011
  }
5899
5012
  try {
@@ -5942,7 +5055,7 @@ async function applyStyleFromCss(opts) {
5942
5055
  propertyValue: styleValue,
5943
5056
  customCssWriteMode: "merge-with-stored"
5944
5057
  });
5945
- dispatchMcpStylesAppliedEvent2({ styleValue });
5058
+ dispatchMcpStylesAppliedEvent({ styleValue });
5946
5059
  } catch (error) {
5947
5060
  throw new Error(
5948
5061
  createUpdateErrorMessage({
@@ -5969,13 +5082,13 @@ Provide styling as raw CSS via the "style" parameter (a flat map of CSS property
5969
5082
  }
5970
5083
 
5971
5084
  // src/mcp/tools/get-element-config/tool.ts
5972
- import { getContainer as getContainer7, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache11 } from "@elementor/editor-elements";
5973
- import { Schema as Schema5 } from "@elementor/editor-props";
5085
+ import { getContainer as getContainer6, getElementStyles as getElementStyles2, getWidgetsCache as getWidgetsCache7 } from "@elementor/editor-elements";
5086
+ import { Schema as Schema3 } from "@elementor/editor-props";
5974
5087
  import { z as z3 } from "@elementor/schema";
5975
5088
  var schema = {
5976
5089
  elementId: z3.string()
5977
5090
  };
5978
- var outputSchema3 = {
5091
+ var outputSchema2 = {
5979
5092
  properties: z3.record(z3.string(), z3.any()).describe("A record mapping PropTypes to their corresponding PropValues"),
5980
5093
  style: z3.record(z3.string(), z3.any()).describe("A record mapping StyleSchema properties to their corresponding PropValues"),
5981
5094
  childElements: z3.array(
@@ -6002,14 +5115,14 @@ var initGetElementConfigTool = (reg) => {
6002
5115
  name: "get-element-configuration-values",
6003
5116
  description: "Retrieve the element's configuration PropValues for a specific element by unique ID.",
6004
5117
  schema,
6005
- outputSchema: outputSchema3,
5118
+ outputSchema: outputSchema2,
6006
5119
  handler: async ({ elementId }) => {
6007
- const element = getContainer7(elementId);
5120
+ const element = getContainer6(elementId);
6008
5121
  if (!element) {
6009
5122
  throw new Error(`Element with ID ${elementId} not found.`);
6010
5123
  }
6011
5124
  const elementType = element.model.get("widgetType") || element.model.get("elType") || "";
6012
- const widgetData = getWidgetsCache11()?.[elementType];
5125
+ const widgetData = getWidgetsCache7()?.[elementType];
6013
5126
  if (!widgetData) {
6014
5127
  throw new Error(
6015
5128
  `Unknown element type: ${elementType}. Check the available-widgets resource for valid types.`
@@ -6021,13 +5134,13 @@ var initGetElementConfigTool = (reg) => {
6021
5134
  );
6022
5135
  }
6023
5136
  const elementRawSettings = element.settings;
6024
- const propSchema = getWidgetsCache11()?.[elementType]?.atomic_props_schema;
5137
+ const propSchema = getWidgetsCache7()?.[elementType]?.atomic_props_schema;
6025
5138
  if (!elementRawSettings || !propSchema) {
6026
5139
  throw new Error(`No settings or prop schema found for element ID: ${elementId}`);
6027
5140
  }
6028
5141
  const propValues = {};
6029
5142
  const stylePropValues = {};
6030
- Schema5.configurableKeys(propSchema).forEach((key) => {
5143
+ Schema3.configurableKeys(propSchema).forEach((key) => {
6031
5144
  propValues[key] = structuredClone(elementRawSettings.get(key));
6032
5145
  });
6033
5146
  const elementStyles = getElementStyles2(elementId) || {};
@@ -6063,7 +5176,7 @@ var initGetElementConfigTool = (reg) => {
6063
5176
 
6064
5177
  // src/mcp/canvas-mcp.ts
6065
5178
  var initCanvasMcp = (reg) => {
6066
- Schema6.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
5179
+ Schema4.setDynamicTagNamesResolver(getDynamicTagNamesByCategories);
6067
5180
  initWidgetsSchemaResource(reg);
6068
5181
  initAvailableWidgetsResource(reg);
6069
5182
  initDocumentStructureResource(reg);
@@ -6071,9 +5184,10 @@ var initCanvasMcp = (reg) => {
6071
5184
  initSelectedElementResource(reg);
6072
5185
  initEditorStateResource(reg);
6073
5186
  initGeneralContextResource(reg);
6074
- initBuildCompositionsTool(reg);
5187
+ initBestPracticesResource(reg);
6075
5188
  initGetElementConfigTool(reg);
6076
5189
  initConfigureElementTool(reg);
5190
+ initBuildCompositionTool(reg);
6077
5191
  initBreakpointsResource(reg);
6078
5192
  };
6079
5193
 
@@ -6273,7 +5387,7 @@ function shouldBlock(sourceElements, targetElements) {
6273
5387
  }
6274
5388
 
6275
5389
  // src/style-commands/paste-style.ts
6276
- import { getContainer as getContainer8, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
5390
+ import { getContainer as getContainer7, getElementSetting, updateElementSettings as updateElementSettings2 } from "@elementor/editor-elements";
6277
5391
  import { classesPropTypeUtil } from "@elementor/editor-props";
6278
5392
  import {
6279
5393
  __privateListenTo as listenTo5,
@@ -6282,7 +5396,7 @@ import {
6282
5396
  } from "@elementor/editor-v1-adapters";
6283
5397
 
6284
5398
  // src/utils/command-utils.ts
6285
- import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache12 } from "@elementor/editor-elements";
5399
+ import { getElementLabel as getElementLabel2, getWidgetsCache as getWidgetsCache8 } from "@elementor/editor-elements";
6286
5400
  import { CLASSES_PROP_KEY } from "@elementor/editor-props";
6287
5401
  import { __ as __5 } from "@wordpress/i18n";
6288
5402
  function hasAtomicWidgets(args) {
@@ -6307,7 +5421,7 @@ function getClassesProp(container) {
6307
5421
  }
6308
5422
  function getContainerSchema(container) {
6309
5423
  const type = container?.model.get("widgetType") || container?.model.get("elType");
6310
- const widgetsCache = getWidgetsCache12();
5424
+ const widgetsCache = getWidgetsCache8();
6311
5425
  const elementType = widgetsCache?.[type];
6312
5426
  return elementType?.atomic_props_schema ?? null;
6313
5427
  }
@@ -6426,7 +5540,7 @@ function pasteStyles(args, pasteLocalStyle) {
6426
5540
  }
6427
5541
  const clipboardElements = getClipboardElements(storageKey);
6428
5542
  const [clipboardElement] = clipboardElements ?? [];
6429
- const clipboardContainer = getContainer8(clipboardElement.id);
5543
+ const clipboardContainer = getContainer7(clipboardElement.id);
6430
5544
  if (!clipboardElement || !clipboardContainer || !isAtomicWidget(clipboardContainer)) {
6431
5545
  return;
6432
5546
  }
@@ -6703,10 +5817,10 @@ function useEscapeOnCanvas(canvasDocument, onEscape) {
6703
5817
  }
6704
5818
 
6705
5819
  // src/utils/after-render.ts
6706
- import { getContainer as getContainer9 } from "@elementor/editor-elements";
5820
+ import { getContainer as getContainer8 } from "@elementor/editor-elements";
6707
5821
  function doAfterRender(elementIds, callback) {
6708
5822
  const pending = elementIds.map((elementId) => {
6709
- const view = getContainer9(elementId)?.view;
5823
+ const view = getContainer8(elementId)?.view;
6710
5824
  if (!view || !hasDoAfterRender(view)) {
6711
5825
  return void 0;
6712
5826
  }
@@ -6721,6 +5835,9 @@ function doAfterRender(elementIds, callback) {
6721
5835
  function hasDoAfterRender(view) {
6722
5836
  return typeof view?._doAfterRender === "function";
6723
5837
  }
5838
+
5839
+ // src/sync/element-added-event.ts
5840
+ var ELEMENT_ADDED_EVENT = "elementor/canvas/element-added";
6724
5841
  export {
6725
5842
  BREAKPOINTS_SCHEMA_FULL_URI,
6726
5843
  BREAKPOINTS_SCHEMA_URI,