@elementor/editor-canvas 4.1.0-838 → 4.1.0-beta2

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.
@@ -1,6 +1,14 @@
1
+ import { toolPrompts } from '@elementor/editor-mcp';
2
+
1
3
  import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
2
4
 
3
- export const configureElementToolPrompt = `Configure an existing element on the page.
5
+ export const CONFIGURE_ELEMENT_GUIDE_URI = 'elementor://canvas/tools/configure-element-guide';
6
+
7
+ export const generatePrompt = () => {
8
+ const configureElementToolPrompt = toolPrompts( 'configure-element' );
9
+
10
+ configureElementToolPrompt.description( `
11
+ Configure an existing element on the page.
4
12
 
5
13
  # **CRITICAL - REQUIRED INFORMATION (Must read before using this tool)**
6
14
  1. [${ WIDGET_SCHEMA_URI }]
@@ -15,12 +23,6 @@ Before using this tool, check the definitions of the elements PropTypes at the r
15
23
  All widgets share a common _style property for styling, which uses the common styles schema.
16
24
  Retrieve and check the common styles schema at the resource list "styles-schema" at editor-canvas__elementor://styles/schema/{category}
17
25
 
18
- # Parameters
19
- - propertiesToChange: An object containing the properties to change, with their new values. MANDATORY. When updating a style only, provide an empty object.
20
- - stylePropertiesToChange: An object containing the style properties to change, with their new values. OPTIONAL
21
- - elementId: The ID of the element to configure. MANDATORY
22
- - elementType: The type of the element to configure (i.e. e-heading, e-button). MANDATORY
23
-
24
26
  # When to use this tool
25
27
  When a user requires to change anything in an element, such as updating text, colors, sizes, or other configurable properties.
26
28
  This tool handles elements of type "widget".
@@ -49,7 +51,7 @@ You can use multiple property changes at once by providing multiple entries in t
49
51
  Some properties are nested, use the root property name, then objects with nested values inside, as the complete schema suggests.
50
52
 
51
53
  Make sure you have the "widget-schema-by-type" resource available to retrieve the PropType schema for the element type you are configuring.
52
- Make sure you have to "styles-schema" resources available to retrieve the common styles schema.
54
+ Make sure you have the "styles-schema" resources available to retrieve the common styles schema.
53
55
 
54
56
  # How to configure elements
55
57
  We use a dedicated PropType Schema for configuring elements, including styles. When you configure an element, you must use the EXACT PropType Value as defined in the schema.
@@ -57,8 +59,26 @@ For styleProperties, use the style schema provided, as it also uses the PropType
57
59
  For all non-primitive types, provide the key property as defined in the schema as $$type in the generated object, as it is MANDATORY for parsing.
58
60
 
59
61
  Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property from the original configuration for every property you are changing.
62
+ ` );
63
+
64
+ configureElementToolPrompt.parameter( 'elementId', 'The ID of the element to configure. MANDATORY.' );
65
+
66
+ configureElementToolPrompt.parameter(
67
+ 'elementType',
68
+ 'The type of the element to configure (i.e. e-heading, e-button). MANDATORY.'
69
+ );
70
+
71
+ configureElementToolPrompt.parameter(
72
+ 'propertiesToChange',
73
+ 'An object containing the properties to change, with their new values. MANDATORY. When updating a style only, provide an empty object.'
74
+ );
75
+
76
+ configureElementToolPrompt.parameter(
77
+ 'stylePropertiesToChange',
78
+ 'An object containing the style properties to change, with their new values. OPTIONAL.'
79
+ );
60
80
 
61
- # Example
81
+ configureElementToolPrompt.example( `
62
82
  \`\`\`json
63
83
  {
64
84
  propertiesToChange: {
@@ -74,7 +94,7 @@ Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property fr
74
94
  },
75
95
  stylePropertiesToChange: {
76
96
  'line-height': {
77
- $$type: 'size', // MANDATORY do not forget to include the correct $$type for every property
97
+ $$type: 'size',
78
98
  value: {
79
99
  size: {
80
100
  $$type: 'number',
@@ -91,8 +111,21 @@ Use the EXACT "PROP-TYPE" Schema given, and ALWAYS include the "key" property fr
91
111
  elementType: 'element-type'
92
112
  };
93
113
  \`\`\`
114
+ ` );
94
115
 
95
- <IMPORTANT>
96
- The $$type property is MANDATORY for every value, it is required to parse the value and apply application-level effects.
97
- </IMPORTANT>
98
- `;
116
+ configureElementToolPrompt.instruction(
117
+ 'The $$type property is MANDATORY for every value; it is required to parse the value and apply application-level effects.'
118
+ );
119
+
120
+ configureElementToolPrompt.instruction( `
121
+ V4 only: If MCP fails, give manual steps using V4 UI.
122
+
123
+ V4 Editor structure:
124
+ Panel tabs: General (→ Settings section: ID, Tag, Link), Style, Interactions.
125
+ NO Advanced tab. Never mention Advanced tab.
126
+ ` );
127
+
128
+ return configureElementToolPrompt.prompt();
129
+ };
130
+
131
+ export const CONFIGURE_ELEMENT_GUIDE_TEXT = generatePrompt();
@@ -4,20 +4,34 @@ import { type MCPRegistryEntry } from '@elementor/editor-mcp';
4
4
  import { STYLE_SCHEMA_URI, WIDGET_SCHEMA_URI } from '../../resources/widgets-schema-resource';
5
5
  import { doUpdateElementProperty } from '../../utils/do-update-element-property';
6
6
  import { validateInput } from '../../utils/validate-input';
7
- import { configureElementToolPrompt } from './prompt';
7
+ import { CONFIGURE_ELEMENT_GUIDE_URI, generatePrompt } from './prompt';
8
8
  import { inputSchema as schema, outputSchema } from './schema';
9
9
 
10
10
  export const initConfigureElementTool = ( reg: MCPRegistryEntry ) => {
11
- const { addTool } = reg;
11
+ const { addTool, resource } = reg;
12
+
13
+ resource(
14
+ 'configure-element-guide',
15
+ CONFIGURE_ELEMENT_GUIDE_URI,
16
+ {
17
+ title: 'Configure Element Guide',
18
+ description: 'Detailed guide for using the configure-element tool',
19
+ mimeType: 'text/plain',
20
+ },
21
+ async ( uri: URL ) => ( {
22
+ contents: [ { uri: uri.href, mimeType: 'text/plain', text: generatePrompt() } ],
23
+ } )
24
+ );
12
25
 
13
26
  addTool( {
14
27
  name: 'configure-element',
15
- description: configureElementToolPrompt,
28
+ description: "Configure an existing V4 element's properties and styles. Read the guide resource before use.",
16
29
  schema,
17
30
  outputSchema,
18
31
  requiredResources: [
19
32
  { description: 'Widgets schema', uri: WIDGET_SCHEMA_URI },
20
33
  { description: 'Styles schema', uri: STYLE_SCHEMA_URI },
34
+ { description: 'Configure element guide', uri: CONFIGURE_ELEMENT_GUIDE_URI },
21
35
  ],
22
36
  modelPreferences: {
23
37
  hints: [ { name: 'claude-sonnet-4-5' } ],
@@ -0,0 +1,135 @@
1
+ import { getWidgetsCache, updateElementSettings } from '@elementor/editor-elements';
2
+ import { Schema } from '@elementor/editor-props';
3
+ import { __privateRunCommandSync } from '@elementor/editor-v1-adapters';
4
+
5
+ import { doUpdateElementProperty } from '../do-update-element-property';
6
+
7
+ jest.mock( '@elementor/editor-elements', () => ( {
8
+ createElementStyle: jest.fn(),
9
+ getElementStyles: jest.fn(),
10
+ getWidgetsCache: jest.fn(),
11
+ updateElementSettings: jest.fn(),
12
+ updateElementStyle: jest.fn(),
13
+ } ) );
14
+
15
+ jest.mock( '@elementor/editor-props', () => ( {
16
+ getPropSchemaFromCache: jest.fn(),
17
+ Schema: {
18
+ adjustLlmPropValueSchema: jest.fn( ( value: unknown ) => value ),
19
+ validatePropValue: jest.fn(),
20
+ },
21
+ } ) );
22
+
23
+ jest.mock( '@elementor/editor-styles', () => ( {
24
+ getStylesSchema: jest.fn( () => ( {} ) ),
25
+ } ) );
26
+
27
+ jest.mock( '@elementor/editor-v1-adapters', () => ( {
28
+ __privateRunCommandSync: jest.fn(),
29
+ } ) );
30
+
31
+ const ELEMENT_ID = 'test-element-id';
32
+ const ELEMENT_TYPE = 'atomic-heading';
33
+ const EXPECTED_JSON_SCHEMA_SNIPPET = '{"type":"object"}';
34
+ const PROPERTY_NAME = 'title';
35
+ const PROP_SCHEMA_ENTRY = { key: 'titlePropKey' };
36
+
37
+ const widgetsCacheFixture = {
38
+ [ ELEMENT_TYPE ]: {
39
+ atomic_props_schema: {
40
+ [ PROPERTY_NAME ]: PROP_SCHEMA_ENTRY,
41
+ },
42
+ },
43
+ };
44
+
45
+ describe( 'doUpdateElementProperty', () => {
46
+ beforeEach( () => {
47
+ Object.assign( window, {
48
+ elementorV2: {
49
+ editorVariables: {
50
+ Utils: {
51
+ globalVariablesLLMResolvers: [],
52
+ },
53
+ },
54
+ },
55
+ } );
56
+ // @ts-ignore: Mock values for test
57
+ jest.mocked( getWidgetsCache ).mockReturnValue( widgetsCacheFixture );
58
+ // @ts-ignore: Mock values for test
59
+ jest.mocked( Schema.adjustLlmPropValueSchema ).mockImplementation( ( value: unknown ) => value );
60
+ jest.mocked( Schema.validatePropValue ).mockReset();
61
+ } );
62
+
63
+ afterEach( () => {
64
+ jest.clearAllMocks();
65
+ } );
66
+
67
+ it( 'throws when Schema.validatePropValue reports invalid PropValue and does not persist', () => {
68
+ // Arrange
69
+ const propertyValue = { invalid: true };
70
+ jest.mocked( Schema.validatePropValue ).mockReturnValue( {
71
+ jsonSchema: EXPECTED_JSON_SCHEMA_SNIPPET,
72
+ valid: false,
73
+ errorMessages: 'error',
74
+ errors: [],
75
+ } );
76
+
77
+ // Act
78
+ let thrown: unknown;
79
+ try {
80
+ doUpdateElementProperty( {
81
+ elementId: ELEMENT_ID,
82
+ elementType: ELEMENT_TYPE,
83
+ propertyName: PROPERTY_NAME,
84
+ propertyValue,
85
+ } );
86
+ } catch ( error ) {
87
+ thrown = error;
88
+ }
89
+
90
+ // Assert
91
+ expect( thrown ).toBeInstanceOf( Error );
92
+ expect( ( thrown as Error ).message ).toMatch( /Invalid PropValue/ );
93
+ expect( ( thrown as Error ).message ).toContain( ELEMENT_ID );
94
+ expect( ( thrown as Error ).message ).toContain( PROP_SCHEMA_ENTRY.key );
95
+ expect( ( thrown as Error ).message ).toContain( EXPECTED_JSON_SCHEMA_SNIPPET );
96
+ expect( Schema.validatePropValue ).toHaveBeenCalledWith( PROP_SCHEMA_ENTRY, propertyValue );
97
+ expect( updateElementSettings ).not.toHaveBeenCalled();
98
+ expect( __privateRunCommandSync ).not.toHaveBeenCalled();
99
+ } );
100
+
101
+ it( 'updates settings when Schema.validatePropValue reports valid PropValue', () => {
102
+ // Arrange
103
+ const propertyValue = 'resolved-title';
104
+ jest.mocked( Schema.validatePropValue ).mockReturnValue( {
105
+ jsonSchema: EXPECTED_JSON_SCHEMA_SNIPPET,
106
+ valid: true,
107
+ errorMessages: [],
108
+ errors: [],
109
+ } );
110
+
111
+ // Act
112
+ doUpdateElementProperty( {
113
+ elementId: ELEMENT_ID,
114
+ elementType: ELEMENT_TYPE,
115
+ propertyName: PROPERTY_NAME,
116
+ propertyValue,
117
+ } );
118
+
119
+ // Assert
120
+ expect( Schema.validatePropValue ).toHaveBeenCalledWith( PROP_SCHEMA_ENTRY, propertyValue );
121
+ expect( updateElementSettings ).toHaveBeenCalledTimes( 1 );
122
+ expect( updateElementSettings ).toHaveBeenCalledWith( {
123
+ id: ELEMENT_ID,
124
+ props: {
125
+ [ PROPERTY_NAME ]: propertyValue,
126
+ },
127
+ withHistory: false,
128
+ } );
129
+ expect( __privateRunCommandSync ).toHaveBeenCalledWith(
130
+ 'document/save/set-is-modified',
131
+ { status: true },
132
+ { internal: true }
133
+ );
134
+ } );
135
+ } );
@@ -7,6 +7,7 @@ import {
7
7
  } from '@elementor/editor-elements';
8
8
  import { getPropSchemaFromCache, type PropValue, Schema, type TransformablePropValue } from '@elementor/editor-props';
9
9
  import { type CustomCss, getStylesSchema } from '@elementor/editor-styles';
10
+ import { __privateRunCommandSync as runCommandSync } from '@elementor/editor-v1-adapters';
10
11
  import { type Utils as IUtils } from '@elementor/editor-variables';
11
12
  import { type z } from '@elementor/schema';
12
13
 
@@ -128,6 +129,14 @@ export const doUpdateElementProperty = ( params: OwnParams ) => {
128
129
  }
129
130
  const propKey = elementPropSchema[ propertyName ].key;
130
131
  const value = resolvePropValue( propertyValue, propKey );
132
+ const { valid, jsonSchema } = Schema.validatePropValue( elementPropSchema[ propertyName ], propertyValue );
133
+ if ( ! valid ) {
134
+ throw new Error(
135
+ `Invalid PropValue for elementId: ${ elementId }. PropKey: ${ propKey }, PropValue: ${ JSON.stringify(
136
+ propertyValue
137
+ ) }\nExpected Schema: ${ jsonSchema }`
138
+ );
139
+ }
131
140
  updateElementSettings( {
132
141
  id: elementId,
133
142
  props: {
@@ -135,4 +144,5 @@ export const doUpdateElementProperty = ( params: OwnParams ) => {
135
144
  },
136
145
  withHistory: false,
137
146
  } );
147
+ runCommandSync( 'document/save/set-is-modified', { status: true }, { internal: true } );
138
148
  };
@@ -17,6 +17,10 @@ export function isWidgetAvailableForLLM( config: V1ElementConfig | undefined ):
17
17
  if ( config.meta?.llm_support === false ) {
18
18
  return false;
19
19
  }
20
+ // TODO: Restore component once working in compositions
21
+ if ( config.title === 'Component' ) {
22
+ return false;
23
+ }
20
24
  if ( config.atomic_props_schema ) {
21
25
  return true;
22
26
  }
@@ -4,5 +4,5 @@ export const GLOBAL_STYLES_IMPORTED_EVENT = 'elementor/global-styles/imported';
4
4
 
5
5
  export type ImportedGlobalStylesPayload = {
6
6
  global_variables?: { data: StyleVariables };
7
- global_classes?: { items: StyleDefinitionsMap; order: StyleDefinitionID[] };
7
+ global_classes?: { added_items: StyleDefinitionsMap; added_items_order: StyleDefinitionID[] };
8
8
  };