@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.
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +151 -76
- package/dist/index.mjs +137 -61
- package/package.json +18 -18
- package/src/composition-builder/composition-builder.ts +35 -18
- package/src/init.tsx +7 -1
- package/src/mcp/canvas-mcp.ts +0 -9
- package/src/mcp/tools/build-composition/prompt.ts +2 -0
- package/src/mcp/tools/build-composition/tool.ts +40 -18
- package/src/mcp/tools/configure-element/prompt.ts +47 -14
- package/src/mcp/tools/configure-element/tool.ts +17 -3
- package/src/mcp/utils/__tests__/do-update-element-property.test.ts +135 -0
- package/src/mcp/utils/do-update-element-property.ts +10 -0
- package/src/mcp/utils/element-data-util.ts +4 -0
- package/src/sync/global-styles-imported-event.ts +1 -1
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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',
|
|
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
|
-
|
|
96
|
-
The $$type property is MANDATORY for every value
|
|
97
|
-
|
|
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 {
|
|
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:
|
|
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?: {
|
|
7
|
+
global_classes?: { added_items: StyleDefinitionsMap; added_items_order: StyleDefinitionID[] };
|
|
8
8
|
};
|