@elementor/editor-canvas 4.2.0-899 → 4.2.0-901

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
@@ -4873,6 +4873,56 @@ var outputSchema = {
4873
4873
  llm_instructions: import_schema.z.string().describe("Instructions what to do next, Important to follow these instructions!").optional()
4874
4874
  };
4875
4875
 
4876
+ // src/mcp/tools/build-composition/xml-leaf-wrapper.ts
4877
+ var DIV_BLOCK_TAG = "e-div-block";
4878
+ var ZERO_SPACING = {
4879
+ $$type: "size",
4880
+ value: {
4881
+ size: {
4882
+ $$type: "number",
4883
+ value: 0
4884
+ },
4885
+ unit: {
4886
+ $$type: "string",
4887
+ value: "px"
4888
+ }
4889
+ }
4890
+ };
4891
+ function adaptLeafRootParams(params) {
4892
+ const doc = new DOMParser().parseFromString(params.xmlStructure, "application/xml");
4893
+ const rootElement = doc.documentElement;
4894
+ if (!isLeafWidget(rootElement.tagName, params.widgetsCache)) {
4895
+ return params;
4896
+ }
4897
+ const wrapperConfigId = getDivBlockWrapperConfigId(params.widgetsCache);
4898
+ return {
4899
+ ...params,
4900
+ xmlStructure: serializeWrapped(doc, rootElement, wrapperConfigId),
4901
+ stylesConfig: {
4902
+ ...params.stylesConfig,
4903
+ [wrapperConfigId]: {
4904
+ margin: ZERO_SPACING,
4905
+ padding: ZERO_SPACING,
4906
+ ...params.stylesConfig[wrapperConfigId]
4907
+ }
4908
+ }
4909
+ };
4910
+ }
4911
+ function getDivBlockWrapperConfigId(widgetsCache) {
4912
+ return widgetsCache[DIV_BLOCK_TAG]?.title ?? DIV_BLOCK_TAG;
4913
+ }
4914
+ function isLeafWidget(tagName, widgetsCache) {
4915
+ return widgetsCache[tagName]?.elType === "widget";
4916
+ }
4917
+ function serializeWrapped(doc, rootElement, wrapperConfigId) {
4918
+ const wrapper = doc.createElement(DIV_BLOCK_TAG);
4919
+ wrapper.setAttribute("configuration-id", wrapperConfigId);
4920
+ wrapper.appendChild(rootElement.cloneNode(true));
4921
+ const wrappedDoc = new DOMParser().parseFromString(`<${DIV_BLOCK_TAG} />`, "application/xml");
4922
+ wrappedDoc.replaceChild(wrapper, wrappedDoc.documentElement);
4923
+ return new XMLSerializer().serializeToString(wrappedDoc);
4924
+ }
4925
+
4876
4926
  // src/mcp/tools/build-composition/tool.ts
4877
4927
  var initBuildCompositionsTool = (reg) => {
4878
4928
  const { addTool, resource } = reg;
@@ -4902,9 +4952,12 @@ var initBuildCompositionsTool = (reg) => {
4902
4952
  { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 }
4903
4953
  ],
4904
4954
  outputSchema,
4905
- handler: async (params) => {
4906
- assertCompositionXmlUsesV4WidgetsOnly(params.xmlStructure);
4907
- const { xmlStructure, elementConfig, stylesConfig, customCSS } = params;
4955
+ handler: async (rawParams) => {
4956
+ assertCompositionXmlUsesV4WidgetsOnly(rawParams.xmlStructure);
4957
+ const { xmlStructure, elementConfig, stylesConfig, customCSS } = adaptLeafRootParams({
4958
+ ...rawParams,
4959
+ widgetsCache: (0, import_editor_elements15.getWidgetsCache)() ?? {}
4960
+ });
4908
4961
  let generatedXML = "";
4909
4962
  const errors = [];
4910
4963
  const rootContainers = [];
@@ -5015,10 +5068,8 @@ function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
5015
5068
  if (widgetData.elType !== "widget") {
5016
5069
  continue;
5017
5070
  }
5018
- if (!widgetData.atomic_props_schema) {
5019
- throw new Error(
5020
- `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${type}`
5021
- );
5071
+ if (!isWidgetAvailableForLLM(widgetData) || !widgetData.atomic_props_schema) {
5072
+ throw new Error(`This tool does not support element type: ${type}`);
5022
5073
  }
5023
5074
  }
5024
5075
  }
package/dist/index.mjs CHANGED
@@ -4859,6 +4859,56 @@ var outputSchema = {
4859
4859
  llm_instructions: z.string().describe("Instructions what to do next, Important to follow these instructions!").optional()
4860
4860
  };
4861
4861
 
4862
+ // src/mcp/tools/build-composition/xml-leaf-wrapper.ts
4863
+ var DIV_BLOCK_TAG = "e-div-block";
4864
+ var ZERO_SPACING = {
4865
+ $$type: "size",
4866
+ value: {
4867
+ size: {
4868
+ $$type: "number",
4869
+ value: 0
4870
+ },
4871
+ unit: {
4872
+ $$type: "string",
4873
+ value: "px"
4874
+ }
4875
+ }
4876
+ };
4877
+ function adaptLeafRootParams(params) {
4878
+ const doc = new DOMParser().parseFromString(params.xmlStructure, "application/xml");
4879
+ const rootElement = doc.documentElement;
4880
+ if (!isLeafWidget(rootElement.tagName, params.widgetsCache)) {
4881
+ return params;
4882
+ }
4883
+ const wrapperConfigId = getDivBlockWrapperConfigId(params.widgetsCache);
4884
+ return {
4885
+ ...params,
4886
+ xmlStructure: serializeWrapped(doc, rootElement, wrapperConfigId),
4887
+ stylesConfig: {
4888
+ ...params.stylesConfig,
4889
+ [wrapperConfigId]: {
4890
+ margin: ZERO_SPACING,
4891
+ padding: ZERO_SPACING,
4892
+ ...params.stylesConfig[wrapperConfigId]
4893
+ }
4894
+ }
4895
+ };
4896
+ }
4897
+ function getDivBlockWrapperConfigId(widgetsCache) {
4898
+ return widgetsCache[DIV_BLOCK_TAG]?.title ?? DIV_BLOCK_TAG;
4899
+ }
4900
+ function isLeafWidget(tagName, widgetsCache) {
4901
+ return widgetsCache[tagName]?.elType === "widget";
4902
+ }
4903
+ function serializeWrapped(doc, rootElement, wrapperConfigId) {
4904
+ const wrapper = doc.createElement(DIV_BLOCK_TAG);
4905
+ wrapper.setAttribute("configuration-id", wrapperConfigId);
4906
+ wrapper.appendChild(rootElement.cloneNode(true));
4907
+ const wrappedDoc = new DOMParser().parseFromString(`<${DIV_BLOCK_TAG} />`, "application/xml");
4908
+ wrappedDoc.replaceChild(wrapper, wrappedDoc.documentElement);
4909
+ return new XMLSerializer().serializeToString(wrappedDoc);
4910
+ }
4911
+
4862
4912
  // src/mcp/tools/build-composition/tool.ts
4863
4913
  var initBuildCompositionsTool = (reg) => {
4864
4914
  const { addTool, resource } = reg;
@@ -4888,9 +4938,12 @@ var initBuildCompositionsTool = (reg) => {
4888
4938
  { description: "Available widgets for this tool", uri: AVAILABLE_WIDGETS_URI_V4 }
4889
4939
  ],
4890
4940
  outputSchema,
4891
- handler: async (params) => {
4892
- assertCompositionXmlUsesV4WidgetsOnly(params.xmlStructure);
4893
- const { xmlStructure, elementConfig, stylesConfig, customCSS } = params;
4941
+ handler: async (rawParams) => {
4942
+ assertCompositionXmlUsesV4WidgetsOnly(rawParams.xmlStructure);
4943
+ const { xmlStructure, elementConfig, stylesConfig, customCSS } = adaptLeafRootParams({
4944
+ ...rawParams,
4945
+ widgetsCache: getWidgetsCache9() ?? {}
4946
+ });
4894
4947
  let generatedXML = "";
4895
4948
  const errors = [];
4896
4949
  const rootContainers = [];
@@ -5001,10 +5054,8 @@ function assertCompositionXmlUsesV4WidgetsOnly(xmlStructure) {
5001
5054
  if (widgetData.elType !== "widget") {
5002
5055
  continue;
5003
5056
  }
5004
- if (!widgetData.atomic_props_schema) {
5005
- throw new Error(
5006
- `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${type}`
5007
- );
5057
+ if (!isWidgetAvailableForLLM(widgetData) || !widgetData.atomic_props_schema) {
5058
+ throw new Error(`This tool does not support element type: ${type}`);
5008
5059
  }
5009
5060
  }
5010
5061
  }
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-899",
4
+ "version": "4.2.0-901",
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-899",
40
+ "@elementor/editor": "4.2.0-901",
41
41
  "dompurify": "^3.2.6",
42
- "@elementor/editor-controls": "4.2.0-899",
43
- "@elementor/editor-documents": "4.2.0-899",
44
- "@elementor/editor-elements": "4.2.0-899",
45
- "@elementor/editor-interactions": "4.2.0-899",
46
- "@elementor/editor-mcp": "4.2.0-899",
47
- "@elementor/editor-notifications": "4.2.0-899",
48
- "@elementor/editor-props": "4.2.0-899",
49
- "@elementor/editor-responsive": "4.2.0-899",
50
- "@elementor/editor-styles": "4.2.0-899",
51
- "@elementor/editor-styles-repository": "4.2.0-899",
52
- "@elementor/editor-ui": "4.2.0-899",
53
- "@elementor/editor-v1-adapters": "4.2.0-899",
54
- "@elementor/schema": "4.2.0-899",
55
- "@elementor/twing": "4.2.0-899",
42
+ "@elementor/editor-controls": "4.2.0-901",
43
+ "@elementor/editor-documents": "4.2.0-901",
44
+ "@elementor/editor-elements": "4.2.0-901",
45
+ "@elementor/editor-interactions": "4.2.0-901",
46
+ "@elementor/editor-mcp": "4.2.0-901",
47
+ "@elementor/editor-notifications": "4.2.0-901",
48
+ "@elementor/editor-props": "4.2.0-901",
49
+ "@elementor/editor-responsive": "4.2.0-901",
50
+ "@elementor/editor-styles": "4.2.0-901",
51
+ "@elementor/editor-styles-repository": "4.2.0-901",
52
+ "@elementor/editor-ui": "4.2.0-901",
53
+ "@elementor/editor-v1-adapters": "4.2.0-901",
54
+ "@elementor/schema": "4.2.0-901",
55
+ "@elementor/twing": "4.2.0-901",
56
56
  "@elementor/ui": "1.37.5",
57
- "@elementor/utils": "4.2.0-899",
58
- "@elementor/wp-media": "4.2.0-899",
57
+ "@elementor/utils": "4.2.0-901",
58
+ "@elementor/wp-media": "4.2.0-901",
59
59
  "@floating-ui/react": "^0.27.5",
60
60
  "@wordpress/i18n": "^5.13.0"
61
61
  },
@@ -0,0 +1,117 @@
1
+ import { type V1ElementConfig } from '@elementor/editor-elements';
2
+
3
+ import { adaptLeafRootParams, type BuildCompositionParams, DIV_BLOCK_TAG, ZERO_SPACING } from '../xml-leaf-wrapper';
4
+
5
+ const LEAF_WIDGET_TAG = 'e-heading';
6
+ const CONTAINER_TAG = 'e-div-block';
7
+ const DIV_BLOCK_TITLE = 'Div Block';
8
+
9
+ const makeWidgetsCache = (
10
+ overrides: Record< string, Partial< V1ElementConfig > > = {}
11
+ ): Record< string, V1ElementConfig > =>
12
+ ( {
13
+ [ LEAF_WIDGET_TAG ]: { title: 'Heading', controls: {}, elType: 'widget' },
14
+ [ CONTAINER_TAG ]: { title: DIV_BLOCK_TITLE, controls: {}, elType: 'e-div-block' },
15
+ ...overrides,
16
+ } ) as Record< string, V1ElementConfig >;
17
+
18
+ const parseXml = ( xml: string ) => new DOMParser().parseFromString( xml, 'application/xml' );
19
+
20
+ const expectElementAttribute = ( element: Element, attribute: string, value: string ) => {
21
+ // eslint-disable-next-line jest-dom/prefer-to-have-attribute -- XML Element, not HTMLElement
22
+ expect( element.getAttribute( attribute ) ).toBe( value );
23
+ };
24
+
25
+ const makeParams = ( xmlStructure: string, overrides = {} ) =>
26
+ ( {
27
+ xmlStructure,
28
+ stylesConfig: {},
29
+ elementConfig: {},
30
+ widgetsCache: makeWidgetsCache(),
31
+ ...overrides,
32
+ } ) as BuildCompositionParams;
33
+
34
+ describe( 'adaptLeafRootParams', () => {
35
+ it( 'wraps a leaf widget root in a div-block', () => {
36
+ // Arrange
37
+ const params = makeParams( `<${ LEAF_WIDGET_TAG } configuration-id="heading-1" />` );
38
+
39
+ // Act
40
+ const result = adaptLeafRootParams( params );
41
+
42
+ // Assert
43
+ const doc = parseXml( result.xmlStructure );
44
+ expect( doc.documentElement.tagName ).toBe( DIV_BLOCK_TAG );
45
+ expect( doc.documentElement.children[ 0 ].tagName ).toBe( LEAF_WIDGET_TAG );
46
+ expectElementAttribute( doc.documentElement, 'configuration-id', DIV_BLOCK_TITLE );
47
+ } );
48
+
49
+ it( 'preserves existing stylesConfig entries when wrapping', () => {
50
+ // Arrange
51
+ const params = makeParams( `<${ LEAF_WIDGET_TAG } configuration-id="heading-1" />`, {
52
+ stylesConfig: { 'heading-1': { color: 'red' } },
53
+ } );
54
+
55
+ // Act
56
+ const result = adaptLeafRootParams( params );
57
+
58
+ // Assert
59
+ expect( result.stylesConfig[ 'heading-1' ] ).toEqual( { color: 'red' } );
60
+ } );
61
+
62
+ it( 'does not wrap when root is a container', () => {
63
+ // Arrange
64
+ const params = makeParams(
65
+ `<${ CONTAINER_TAG } configuration-id="container-1"><${ LEAF_WIDGET_TAG } configuration-id="heading-1" /></${ CONTAINER_TAG }>`
66
+ );
67
+
68
+ // Act
69
+ const result = adaptLeafRootParams( params );
70
+
71
+ // Assert
72
+ expect( result ).toBe( params );
73
+ } );
74
+
75
+ it( 'preserves extra params fields unchanged', () => {
76
+ // Arrange
77
+ const params = makeParams( `<${ LEAF_WIDGET_TAG } configuration-id="heading-1" />`, {
78
+ elementConfig: { 'heading-1': { title: 'Hello' } },
79
+ stylesConfig: { 'heading-1': { color: 'red' } },
80
+ } );
81
+
82
+ // Act
83
+ const result = adaptLeafRootParams( params );
84
+
85
+ // Assert
86
+ expect( result.stylesConfig ).toEqual( {
87
+ 'Div Block': {
88
+ margin: ZERO_SPACING,
89
+ padding: ZERO_SPACING,
90
+ },
91
+ ...params.stylesConfig,
92
+ } );
93
+ } );
94
+
95
+ it( 'preserves leaf element attributes when wrapping', () => {
96
+ // Arrange
97
+ const params = makeParams( `<${ LEAF_WIDGET_TAG } configuration-id="heading-1" />` );
98
+
99
+ // Act
100
+ const { xmlStructure } = adaptLeafRootParams( params );
101
+
102
+ // Assert
103
+ const doc = parseXml( xmlStructure );
104
+ expectElementAttribute( doc.documentElement.children[ 0 ], 'configuration-id', 'heading-1' );
105
+ } );
106
+
107
+ it( 'returns unchanged params for unknown element types', () => {
108
+ // Arrange
109
+ const params = makeParams( '<unknown-widget configuration-id="w1" />' );
110
+
111
+ // Act
112
+ const result = adaptLeafRootParams( params );
113
+
114
+ // Assert
115
+ expect( result ).toBe( params );
116
+ } );
117
+ } );
@@ -12,9 +12,11 @@ import { CompositionBuilder } from '../../../composition-builder/composition-bui
12
12
  import { AVAILABLE_WIDGETS_URI_V4 } from '../../resources/available-widgets-resource';
13
13
  import { BEST_PRACTICES_URI, STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
14
14
  import { doUpdateElementProperty } from '../../utils/do-update-element-property';
15
+ import { isWidgetAvailableForLLM } from '../../utils/element-data-util';
15
16
  import { getCompositionTargetContainer } from '../../utils/get-composition-target-container';
16
17
  import { BUILD_COMPOSITIONS_GUIDE_URI, generatePrompt } from './prompt';
17
18
  import { inputSchema as schema, outputSchema } from './schema';
19
+ import { adaptLeafRootParams } from './xml-leaf-wrapper';
18
20
 
19
21
  export const initBuildCompositionsTool = ( reg: MCPRegistryEntry ) => {
20
22
  const { addTool, resource } = reg;
@@ -46,9 +48,13 @@ export const initBuildCompositionsTool = ( reg: MCPRegistryEntry ) => {
46
48
  { description: 'Available widgets for this tool', uri: AVAILABLE_WIDGETS_URI_V4 },
47
49
  ],
48
50
  outputSchema,
49
- handler: async ( params ) => {
50
- assertCompositionXmlUsesV4WidgetsOnly( params.xmlStructure );
51
- const { xmlStructure, elementConfig, stylesConfig, customCSS } = params;
51
+ handler: async ( rawParams ) => {
52
+ assertCompositionXmlUsesV4WidgetsOnly( rawParams.xmlStructure );
53
+ const { xmlStructure, elementConfig, stylesConfig, customCSS } = adaptLeafRootParams( {
54
+ ...rawParams,
55
+ widgetsCache: getWidgetsCache() ?? {},
56
+ } );
57
+
52
58
  let generatedXML: string = '';
53
59
  const errors: Error[] = [];
54
60
  const rootContainers: V1Element[] = [];
@@ -164,16 +170,15 @@ function assertCompositionXmlUsesV4WidgetsOnly( xmlStructure: string ) {
164
170
  for ( const node of doc.querySelectorAll( '*' ) ) {
165
171
  const type = node.tagName;
166
172
  const widgetData = widgetsCache[ type ];
173
+
167
174
  if ( ! widgetData ) {
168
175
  continue;
169
176
  }
170
177
  if ( widgetData.elType !== 'widget' ) {
171
178
  continue;
172
179
  }
173
- if ( ! widgetData.atomic_props_schema ) {
174
- throw new Error(
175
- `This tool does not support V3 elements. Please use the elementor-v3-mcp tools instead for element type: ${ type }`
176
- );
180
+ if ( ! isWidgetAvailableForLLM( widgetData ) || ! widgetData.atomic_props_schema ) {
181
+ throw new Error( `This tool does not support element type: ${ type }` );
177
182
  }
178
183
  }
179
184
  }
@@ -0,0 +1,68 @@
1
+ import { type V1ElementConfig } from '@elementor/editor-elements';
2
+
3
+ export const DIV_BLOCK_TAG = 'e-div-block';
4
+
5
+ export const ZERO_SPACING = {
6
+ $$type: 'size',
7
+ value: {
8
+ size: {
9
+ $$type: 'number',
10
+ value: 0,
11
+ },
12
+ unit: {
13
+ $$type: 'string',
14
+ value: 'px',
15
+ },
16
+ },
17
+ };
18
+
19
+ export type BuildCompositionParams = {
20
+ xmlStructure: string;
21
+ stylesConfig: Record< string, Record< string, unknown > >;
22
+ widgetsCache: Record< string, V1ElementConfig >;
23
+ elementConfig: Record< string, Record< string, unknown > >;
24
+ [ key: string ]: unknown;
25
+ };
26
+
27
+ export function adaptLeafRootParams< T extends BuildCompositionParams >( params: T ): T {
28
+ const doc = new DOMParser().parseFromString( params.xmlStructure, 'application/xml' );
29
+ const rootElement = doc.documentElement;
30
+
31
+ if ( ! isLeafWidget( rootElement.tagName, params.widgetsCache ) ) {
32
+ return params;
33
+ }
34
+
35
+ const wrapperConfigId = getDivBlockWrapperConfigId( params.widgetsCache );
36
+
37
+ return {
38
+ ...params,
39
+ xmlStructure: serializeWrapped( doc, rootElement, wrapperConfigId ),
40
+ stylesConfig: {
41
+ ...params.stylesConfig,
42
+ [ wrapperConfigId ]: {
43
+ margin: ZERO_SPACING,
44
+ padding: ZERO_SPACING,
45
+ ...params.stylesConfig[ wrapperConfigId ],
46
+ },
47
+ },
48
+ };
49
+ }
50
+
51
+ function getDivBlockWrapperConfigId( widgetsCache: Record< string, V1ElementConfig > ): string {
52
+ return widgetsCache[ DIV_BLOCK_TAG ]?.title ?? DIV_BLOCK_TAG;
53
+ }
54
+
55
+ function isLeafWidget( tagName: string, widgetsCache: Record< string, V1ElementConfig > ): boolean {
56
+ return widgetsCache[ tagName ]?.elType === 'widget';
57
+ }
58
+
59
+ function serializeWrapped( doc: Document, rootElement: Element, wrapperConfigId: string ): string {
60
+ const wrapper = doc.createElement( DIV_BLOCK_TAG );
61
+ wrapper.setAttribute( 'configuration-id', wrapperConfigId );
62
+ wrapper.appendChild( rootElement.cloneNode( true ) );
63
+
64
+ const wrappedDoc = new DOMParser().parseFromString( `<${ DIV_BLOCK_TAG } />`, 'application/xml' );
65
+ wrappedDoc.replaceChild( wrapper, wrappedDoc.documentElement );
66
+
67
+ return new XMLSerializer().serializeToString( wrappedDoc );
68
+ }